From 3ebbd2ed3904aca95fc5932d1408212cbd93bc6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 16 May 2021 11:26:43 +0200 Subject: [PATCH 01/93] doc: suggest StaticArray as another replacement for the deprecated Array. --- doc/changelog.dox | 5 ++++- src/Magnum/Array.h | 15 +++++++++------ src/Magnum/Magnum.h | 6 +++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 0ce99357e..f2ae999e5 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -444,7 +444,10 @@ See also: It was also commonly confused with @relativeref{Corrade,Containers::Array}, which is a type with totally different semantics. To avoid breaking existing code, conversion from and to @ref Math::Vector is now provided and - this type is included in all places where it was originally used. + this type is included in all places where it was originally used. For + custom uses, the @relativeref{Corrade,Containers::Array1}, @relativeref{Corrade,Containers::Array2} or + @relativeref{Corrade,Containers::Array3} types provide a suitable + alternative as well. - Markup styling for Emscripten application was switched to prefer using CSS classes instead of the @cb{.css} #container @ce, @cb{.css} #sizer @ce, @cb{.css} #expander @ce, @cb{.css} #listener @ce, @cb{.css} #canvas @ce, diff --git a/src/Magnum/Array.h b/src/Magnum/Array.h index cb115ae1d..0eb34dc0b 100644 --- a/src/Magnum/Array.h +++ b/src/Magnum/Array.h @@ -141,9 +141,10 @@ CORRADE_IGNORE_DEPRECATED_PUSH /** @brief One-dimensional array @tparam T Data type -@m_deprecated_since_latest Use @ref Math::Vector instead. +@m_deprecated_since_latest Use @ref Math::Vector or @ref Containers::Array1 + instead. */ -template class CORRADE_DEPRECATED("use Math::Vector instead") Array1D: public Array<1, T> { +template class CORRADE_DEPRECATED("use Math::Vector or Containers::Array1 instead") Array1D: public Array<1, T> { public: /** @copydoc Array::Array() */ constexpr /*implicit*/ Array1D() = default; @@ -164,9 +165,10 @@ template class CORRADE_DEPRECATED("use Math::Vector instead") Array1D: /** @brief Two-dimensional array @tparam T Data type -@m_deprecated_since_latest Use @ref Math::Vector2 instead. +@m_deprecated_since_latest Use @ref Math::Vector2 or @ref Containers::Array2 + instead. */ -template class CORRADE_DEPRECATED("use Math::Vector2 instead") Array2D: public Array<2, T> { +template class CORRADE_DEPRECATED("use Math::Vector2 or Containers::Array2 instead") Array2D: public Array<2, T> { public: /** @copydoc Array::Array() */ constexpr /*implicit*/ Array2D() = default; @@ -198,9 +200,10 @@ template class CORRADE_DEPRECATED("use Math::Vector2 instead") Array2D /** @brief Three-dimensional array @tparam T Data type -@m_deprecated_since_latest Use @ref Math::Vector3 instead. +@m_deprecated_since_latest Use @ref Math::Vector3 or @ref Containers::Array3 + instead. */ -template class CORRADE_DEPRECATED("use Math::Vector3 instead") Array3D: public Array<3, T> { +template class CORRADE_DEPRECATED("use Math::Vector3 or Containers::Array3 instead") Array3D: public Array<3, T> { public: /** @copydoc Array::Array() */ constexpr /*implicit*/ Array3D() {} diff --git a/src/Magnum/Magnum.h b/src/Magnum/Magnum.h index 2d39b24aa..82fc79e15 100644 --- a/src/Magnum/Magnum.h +++ b/src/Magnum/Magnum.h @@ -1171,9 +1171,9 @@ typedef Math::Frustum Frustumd; #ifndef DOXYGEN_GENERATING_OUTPUT #ifdef MAGNUM_BUILD_DEPRECATED template class CORRADE_DEPRECATED("use Math::Vector instead") Array; -template class CORRADE_DEPRECATED("use Math::Vector instead") Array1D; -template class CORRADE_DEPRECATED("use Math::Vector2 instead") Array2D; -template class CORRADE_DEPRECATED("use Math::Vector3 instead") Array3D; +template class CORRADE_DEPRECATED("use Math::Vector or Containers::Array1 instead") Array1D; +template class CORRADE_DEPRECATED("use Math::Vector2 or Containers::Array2 instead") Array2D; +template class CORRADE_DEPRECATED("use Math::Vector3 or Containers::Array3 instead") Array3D; #endif enum class InputFileCallbackPolicy: UnsignedByte; From 686648f120ab94a2a74fdf5906303e357be1e3d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 12 May 2021 19:28:08 +0200 Subject: [PATCH 02/93] Platform: suggest that bundled GLFW should have buncha things disabled. The so-far-perfect impression of GLFW being a heaven to integrate just got a bunch of tiny cracks. Good thing it's being fixed for the next version, although I'd personally disable the docs as well. --- src/Magnum/Platform/GlfwApplication.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Magnum/Platform/GlfwApplication.h b/src/Magnum/Platform/GlfwApplication.h index c8f45615b..07d7c9a93 100644 --- a/src/Magnum/Platform/GlfwApplication.h +++ b/src/Magnum/Platform/GlfwApplication.h @@ -116,6 +116,10 @@ the first part and point `CMAKE_PREFIX_PATH` to its installation dir if necessary. @code{.cmake} +set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) +# These two will be off-by-default when GLFW 3.4 gets released +set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) +set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) add_subdirectory(glfw EXCLUDE_FROM_ALL) set(WITH_GLFWAPPLICATION ON CACHE BOOL "" FORCE) From 4348ca7cdd59f2c0bb8212ba43f9d1f8b9d3bf1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 16 May 2021 11:13:57 +0200 Subject: [PATCH 03/93] Enable a bunch of useful CMake policies. --- CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index eb365ee5f..a27d12722 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,11 +30,19 @@ if(HUNTER_ENABLED) include(${CMAKE_CURRENT_LIST_DIR}/package/hunter/HunterInit.cmake) endif() +# Don't restrict INTERPROCEDURAL_OPTIMIZATION only for icc on Linux +if(POLICY CMP0069) + cmake_policy(SET CMP0069 NEW) +endif() # If CMAKE_AUTOMOC is set, all uses of corrade_add_resource() would otherwise # complain on 3.10 that AUTOMOC is not processing GENERATED files if(POLICY CMP0071) cmake_policy(SET CMP0071 NEW) endif() +# Superprojects can use just set(WITH_BLAH ON) without FORCE CACHE on 3.13+ +if(POLICY CMP0077) + cmake_policy(SET CMP0077 NEW) +endif() project(Magnum CXX) From 088a769f8e3ccca759e3bec0526f401b29889b5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 20 May 2021 16:47:47 +0200 Subject: [PATCH 04/93] GL: adapt ES-specific MeshView internals to Corrade changes. --- src/Magnum/GL/MeshView.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Magnum/GL/MeshView.cpp b/src/Magnum/GL/MeshView.cpp index 19c4fffc0..73717072f 100644 --- a/src/Magnum/GL/MeshView.cpp +++ b/src/Magnum/GL/MeshView.cpp @@ -174,8 +174,8 @@ void MeshView::multiDrawElementsBaseVertexImplementationANGLE(const GLenum mode, Containers::ArrayView instanceCount; Containers::ArrayView baseInstance; Containers::ArrayTuple data{ - {Containers::NoInit, std::size_t(drawCount), instanceCount}, - {Containers::ValueInit, std::size_t(drawCount), baseInstance}, + {NoInit, std::size_t(drawCount), instanceCount}, + {ValueInit, std::size_t(drawCount), baseInstance}, }; for(GLsizei& i: instanceCount) i = 1; From ee8211359051d8430af17aa02a087e88730dcabe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 25 May 2021 00:15:29 +0200 Subject: [PATCH 05/93] GL: this isn't needed on Apple platforms either. --- src/Magnum/GL/Implementation/driverSpecific.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magnum/GL/Implementation/driverSpecific.cpp b/src/Magnum/GL/Implementation/driverSpecific.cpp index 6f999a175..27b899907 100644 --- a/src/Magnum/GL/Implementation/driverSpecific.cpp +++ b/src/Magnum/GL/Implementation/driverSpecific.cpp @@ -415,7 +415,7 @@ auto Context::detectedDriver() -> DetectedDrivers { _detectedDrivers = DetectedDrivers{}; - #ifndef MAGNUM_TARGET_WEBGL + #if !defined(MAGNUM_TARGET_WEBGL) && !defined(CORRADE_TARGET_APPLE) const Containers::StringView renderer = rendererString(); #endif #if !defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_WEBGL) From d3e11765ac3b73ba6737f89512ee9b636cef825d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 16 May 2021 13:49:43 +0200 Subject: [PATCH 06/93] Shaders: EXPLICIT_TEXTURE_LAYER is very misleading. I went through renaming this on many places quite some time ago, but this one slipped through. Now that UBOs will be a thing, rename to EXPLICIT_BINDING instead of EXPLICIT_UNIFORM_BINDING. --- src/Magnum/Shaders/DistanceFieldVector.frag | 2 +- src/Magnum/Shaders/Flat.frag | 2 +- src/Magnum/Shaders/MeshVisualizer.frag | 2 +- src/Magnum/Shaders/Phong.frag | 8 ++++---- src/Magnum/Shaders/Vector.frag | 2 +- src/Magnum/Shaders/compatibility.glsl | 4 ++-- src/Magnum/TextureTools/DistanceFieldShader.frag | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Magnum/Shaders/DistanceFieldVector.frag b/src/Magnum/Shaders/DistanceFieldVector.frag index 78b1b89f0..51a79e78e 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.frag +++ b/src/Magnum/Shaders/DistanceFieldVector.frag @@ -65,7 +65,7 @@ uniform lowp float smoothness /* Textures */ -#ifdef EXPLICIT_TEXTURE_LAYER +#ifdef EXPLICIT_BINDING /* See AbstractVector.h for details about the ID */ layout(binding = 6) #endif diff --git a/src/Magnum/Shaders/Flat.frag b/src/Magnum/Shaders/Flat.frag index 21d3fbf45..995b2bacb 100644 --- a/src/Magnum/Shaders/Flat.frag +++ b/src/Magnum/Shaders/Flat.frag @@ -66,7 +66,7 @@ uniform highp uint objectId; /* defaults to zero */ /* Textures */ #ifdef TEXTURED -#ifdef EXPLICIT_TEXTURE_LAYER +#ifdef EXPLICIT_BINDING layout(binding = 0) #endif uniform lowp sampler2D textureData; diff --git a/src/Magnum/Shaders/MeshVisualizer.frag b/src/Magnum/Shaders/MeshVisualizer.frag index c206409bd..7b7fb5e0b 100644 --- a/src/Magnum/Shaders/MeshVisualizer.frag +++ b/src/Magnum/Shaders/MeshVisualizer.frag @@ -113,7 +113,7 @@ uniform lowp vec2 colorMapOffsetScale /* Textures */ #if defined(INSTANCED_OBJECT_ID) || defined(VERTEX_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID) -#ifdef EXPLICIT_TEXTURE_LAYER +#ifdef EXPLICIT_BINDING layout(binding = 4) #endif uniform lowp sampler2D colorMapTexture; diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index 7f276f0a7..ca6fefe01 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -148,7 +148,7 @@ uniform lowp float lightRanges[LIGHT_COUNT] /* Textures */ #ifdef AMBIENT_TEXTURE -#ifdef EXPLICIT_TEXTURE_LAYER +#ifdef EXPLICIT_BINDING layout(binding = 0) #endif uniform lowp sampler2D ambientTexture; @@ -156,21 +156,21 @@ uniform lowp sampler2D ambientTexture; #if LIGHT_COUNT #ifdef DIFFUSE_TEXTURE -#ifdef EXPLICIT_TEXTURE_LAYER +#ifdef EXPLICIT_BINDING layout(binding = 1) #endif uniform lowp sampler2D diffuseTexture; #endif #ifdef SPECULAR_TEXTURE -#ifdef EXPLICIT_TEXTURE_LAYER +#ifdef EXPLICIT_BINDING layout(binding = 2) #endif uniform lowp sampler2D specularTexture; #endif #ifdef NORMAL_TEXTURE -#ifdef EXPLICIT_TEXTURE_LAYER +#ifdef EXPLICIT_BINDING layout(binding = 3) #endif uniform lowp sampler2D normalTexture; diff --git a/src/Magnum/Shaders/Vector.frag b/src/Magnum/Shaders/Vector.frag index a10ac4931..ec5da58c5 100644 --- a/src/Magnum/Shaders/Vector.frag +++ b/src/Magnum/Shaders/Vector.frag @@ -45,7 +45,7 @@ uniform lowp vec4 color #endif ; -#ifdef EXPLICIT_TEXTURE_LAYER +#ifdef EXPLICIT_BINDING /* See AbstractVector.h for details about the ID */ layout(binding = 6) #endif diff --git a/src/Magnum/Shaders/compatibility.glsl b/src/Magnum/Shaders/compatibility.glsl index fa09c4b59..8c92c6859 100644 --- a/src/Magnum/Shaders/compatibility.glsl +++ b/src/Magnum/Shaders/compatibility.glsl @@ -35,7 +35,7 @@ #if !defined(GL_ES) && defined(GL_ARB_shading_language_420pack) && !defined(DISABLE_GL_ARB_shading_language_420pack) #extension GL_ARB_shading_language_420pack: enable #define RUNTIME_CONST - #define EXPLICIT_TEXTURE_LAYER + #define EXPLICIT_BINDING #endif #if !defined(GL_ES) && defined(GL_ARB_explicit_uniform_location) && !defined(DISABLE_GL_ARB_explicit_uniform_location) @@ -45,7 +45,7 @@ #if defined(GL_ES) && __VERSION__ >= 300 #define EXPLICIT_ATTRIB_LOCATION - /* EXPLICIT_TEXTURE_LAYER, EXPLICIT_UNIFORM_LOCATION and RUNTIME_CONST is not + /* EXPLICIT_BINDING, EXPLICIT_UNIFORM_LOCATION and RUNTIME_CONST is not available in OpenGL ES */ #endif diff --git a/src/Magnum/TextureTools/DistanceFieldShader.frag b/src/Magnum/TextureTools/DistanceFieldShader.frag index 7e9cfe3ae..f5e44fcfc 100644 --- a/src/Magnum/TextureTools/DistanceFieldShader.frag +++ b/src/Magnum/TextureTools/DistanceFieldShader.frag @@ -42,7 +42,7 @@ layout(location = 0) #endif uniform mediump vec2 scaling; -#ifdef EXPLICIT_TEXTURE_LAYER +#ifdef EXPLICIT_BINDING layout(binding = 7) #endif uniform lowp sampler2D textureData; From 94f0dcb421d98bf497bde018aebf34eafa0a9777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 12 May 2021 22:30:17 +0200 Subject: [PATCH 07/93] Shaders: this thing is ... not used anywhere. --- src/Magnum/Shaders/Test/PhongGLTest.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 1d0d031cb..21c8adada 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -1669,8 +1669,6 @@ void PhongGLTest::renderZeroLights() { .setStorage(1, TextureFormatRGBA, ambientImage->size()) .setSubImage(0, {}, *ambientImage); - GL::Texture2D bogus; - shader .bindAmbientTexture(ambient) .setAmbientColor(0x9999ff_rgbf) From 47e7de47c85ffbc3841ab9c0c702345c161c9203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 17 May 2021 12:29:07 +0200 Subject: [PATCH 08/93] Shaders: remove the AbstractVector base. 167 additions, 341 deletions. I think that speaks for itself about benefits of this base class. --- doc/changelog.dox | 28 ++-- src/Magnum/Shaders/AbstractVector.h | 70 --------- src/Magnum/Shaders/AbstractVectorGL.cpp | 43 ------ src/Magnum/Shaders/AbstractVectorGL.h | 134 ------------------ src/Magnum/Shaders/CMakeLists.txt | 3 - src/Magnum/Shaders/DistanceFieldVector.frag | 1 - src/Magnum/Shaders/DistanceFieldVectorGL.cpp | 47 +++--- src/Magnum/Shaders/DistanceFieldVectorGL.h | 70 ++++++--- src/Magnum/Shaders/Shaders.h | 9 -- src/Magnum/Shaders/Vector.frag | 1 - .../{AbstractVector.vert => Vector.vert} | 0 src/Magnum/Shaders/VectorGL.cpp | 38 +++-- src/Magnum/Shaders/VectorGL.h | 60 ++++++-- src/Magnum/Shaders/resources-gl.conf | 6 +- src/Magnum/Text/Renderer.cpp | 13 +- src/Magnum/Text/Renderer.h | 7 +- 16 files changed, 178 insertions(+), 352 deletions(-) delete mode 100644 src/Magnum/Shaders/AbstractVector.h delete mode 100644 src/Magnum/Shaders/AbstractVectorGL.cpp delete mode 100644 src/Magnum/Shaders/AbstractVectorGL.h rename src/Magnum/Shaders/{AbstractVector.vert => Vector.vert} (100%) diff --git a/doc/changelog.dox b/doc/changelog.dox index f2ae999e5..11b5cf82a 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -462,19 +462,17 @@ See also: @ref DebugTools::FrameProfilerGL. The new name plays better with IDE autocompletion and makes the GL-specific class appear next to the API-independent base in alphabetically sorted lists. -- @cpp Shaders::AbstractVector @ce, @cpp Shaders::DistanceFieldVector @ce, - @cpp Shaders::Flat @ce, @cpp Shaders::Generic @ce, - @cpp Shaders::MeshVisualizer2D @ce, @cpp Shaders::MeshVisualizer3D @ce, - @cpp Shaders::Phong @ce, @cpp Shaders::Vector @ce, - @cpp Shaders::VertexColor @ce and related 2D/3D typedefs are deprecated in - favor of @ref Shaders::AbstractVectorGL, - @ref Shaders::DistanceFieldVectorGL, @ref Shaders::FlatGL, - @ref Shaders::GenericGL, @ref Shaders::MeshVisualizerGL2D, - @ref Shaders::MeshVisualizerGL3D, @ref Shaders::PhongGL, - @ref Shaders::VectorGL, @ref Shaders::VertexColorGL and correspondingly - renamed typedefs to make room for Vulkan shaders and functionality shared - between OpenGL and Vulkan implementation such as uniform buffer layout - definitions +- @cpp Shaders::DistanceFieldVector @ce, @cpp Shaders::Flat @ce, + @cpp Shaders::Generic @ce, @cpp Shaders::MeshVisualizer2D @ce, + @cpp Shaders::MeshVisualizer3D @ce, @cpp Shaders::Phong @ce, + @cpp Shaders::Vector @ce, @cpp Shaders::VertexColor @ce and related 2D/3D + typedefs are deprecated in favor of @ref Shaders::DistanceFieldVectorGL, + @ref Shaders::FlatGL, @ref Shaders::GenericGL, + @ref Shaders::MeshVisualizerGL2D, @ref Shaders::MeshVisualizerGL3D, + @ref Shaders::PhongGL, @ref Shaders::VectorGL, @ref Shaders::VertexColorGL + and correspondingly renamed typedefs to make room for Vulkan shaders and + functionality shared between OpenGL and Vulkan implementation such as + uniform buffer layout definitions - @ref Shaders::PhongGL::setLightPositions() and @ref Shaders::PhongGL::setLightPosition() taking three-component vectors are deprecated in favor of variants taking four-component vectors, where the @@ -658,6 +656,10 @@ See also: isn't available anymore. For backwards compatibility, light positions supplied through three-component vectors are now represented as directional lights, which is close, but not exactly the same as before. +- The @cpp Shaders::AbstractVector @ce base class for @ref Shaders::VectorGL + and @ref Shaders::DistanceFieldVectorGL is removed, as its benefits were + rather questionable --- on the contrary, it made subclass implementation + more verbose and less clear - Mutable access to @ref Trade::PhongMaterialData color and texture information, deprecated in 2020.06, is now removed, as it's impossible to implement through the redesigned @ref Trade::MaterialData APIs. However diff --git a/src/Magnum/Shaders/AbstractVector.h b/src/Magnum/Shaders/AbstractVector.h deleted file mode 100644 index 192371879..000000000 --- a/src/Magnum/Shaders/AbstractVector.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef Magnum_Shaders_AbstractVector_h -#define Magnum_Shaders_AbstractVector_h -/* - This file is part of Magnum. - - Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, - 2020, 2021 Vladimír Vondruš - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. -*/ - -#ifdef MAGNUM_BUILD_DEPRECATED -/** @file - * @brief Typedef @ref Magnum::Shaders::AbstractVector, alias @ref Magnum::Shaders::AbstractVector2D, @ref Magnum::Shaders::AbstractVector3D - * @m_deprecated_since_latest Use @ref Magnum/Shaders/AbstractVectorGL.h, the - * @ref Magnum::Shaders::AbstractVectorGL "AbstractVectorGL" class and - * related typedefs instead. - */ -#endif - -#include "Magnum/configure.h" - -#ifdef MAGNUM_BUILD_DEPRECATED -#include - -#include "Magnum/Shaders/AbstractVectorGL.h" - -CORRADE_DEPRECATED_FILE("use Magnum/Shaders/AbstractVectorGL.h, the AbstractVectorGL class and related typedefs instead") - -namespace Magnum { namespace Shaders { - -/** @brief @copybrief AbstractVectorGL - * @m_deprecated_since_latest Use @ref AbstractVectorGL instead. - */ -#ifndef CORRADE_MSVC2015_COMPATIBILITY /* Multiple definitions still broken */ -template using AbstractVector CORRADE_DEPRECATED_ALIAS("use AbstractVectorGL instead") = AbstractVectorGL; -#endif - -/** @brief @copybrief AbstractVectorGL2D - * @m_deprecated_since_latest Use @ref AbstractVectorGL2D instead. - */ -typedef CORRADE_DEPRECATED("use AbstractVectorGL2D instead") AbstractVectorGL2D AbstractVector2D; - -/** @brief @copybrief AbstractVectorGL3D - * @m_deprecated_since_latest Use @ref AbstractVectorGL3D instead. - */ -typedef CORRADE_DEPRECATED("use AbstractVectorGL3D instead") AbstractVectorGL3D AbstractVector3D; - -}} -#else -#error use Magnum/Shaders/AbstractVectorGL.h, the AbstractVectorGL class and related typedefs instead -#endif - -#endif diff --git a/src/Magnum/Shaders/AbstractVectorGL.cpp b/src/Magnum/Shaders/AbstractVectorGL.cpp deleted file mode 100644 index e02251094..000000000 --- a/src/Magnum/Shaders/AbstractVectorGL.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - This file is part of Magnum. - - Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, - 2020, 2021 Vladimír Vondruš - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. -*/ - -#include "AbstractVectorGL.h" - -#include "Magnum/GL/Texture.h" -#include "Magnum/Shaders/visibility.h" - -namespace Magnum { namespace Shaders { - -template AbstractVectorGL& AbstractVectorGL::bindVectorTexture(GL::Texture2D& texture) { - texture.bind(VectorTextureUnit); - return *this; -} - -#ifndef DOXYGEN_GENERATING_OUTPUT -template class MAGNUM_SHADERS_EXPORT AbstractVectorGL<2>; -template class MAGNUM_SHADERS_EXPORT AbstractVectorGL<3>; -#endif - -}} diff --git a/src/Magnum/Shaders/AbstractVectorGL.h b/src/Magnum/Shaders/AbstractVectorGL.h deleted file mode 100644 index 95933640d..000000000 --- a/src/Magnum/Shaders/AbstractVectorGL.h +++ /dev/null @@ -1,134 +0,0 @@ -#ifndef Magnum_Shaders_AbstractVectorGL_h -#define Magnum_Shaders_AbstractVectorGL_h -/* - This file is part of Magnum. - - Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, - 2020, 2021 Vladimír Vondruš - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. -*/ - -/** @file - * @brief Class @ref Magnum::Shaders::AbstractVectorGL, typedef @ref Magnum::Shaders::AbstractVectorGL2D, @ref Magnum::Shaders::AbstractVectorGL3D - * @m_since_latest - */ - -#include "Magnum/GL/AbstractShaderProgram.h" -#include "Magnum/Shaders/GenericGL.h" - -namespace Magnum { namespace Shaders { - -/** -@brief Base for vector OpenGL shaders -@m_since_latest - -See @ref DistanceFieldVectorGL and @ref VectorGL for more information. -@see @ref shaders, @ref AbstractVectorGL2D, @ref AbstractVectorGL3D -*/ -template class AbstractVectorGL: public GL::AbstractShaderProgram { - public: - /** - * @brief Vertex position - * - * @ref shaders-generic "Generic attribute", - * @ref Magnum::Vector2 "Vector2" in 2D, @ref Magnum::Vector3 "Vector3" - * in 3D. - */ - typedef typename GenericGL::Position Position; - - /** - * @brief 2D texture coordinates - * - * @ref shaders-generic "Generic attribute", - * @ref Magnum::Vector2 "Vector2". - */ - typedef typename GenericGL::TextureCoordinates TextureCoordinates; - - enum: UnsignedInt { - /** - * Color shader output. @ref shaders-generic "Generic output", - * present always. Expects three- or four-component floating-point - * or normalized buffer attachment. - */ - ColorOutput = GenericGL::ColorOutput - }; - - /** @brief Copying is not allowed */ - AbstractVectorGL(const AbstractVectorGL&) = delete; - - /** @brief Move constructor */ - AbstractVectorGL(AbstractVectorGL&&) noexcept = default; - - /** @brief Copying is not allowed */ - AbstractVectorGL& operator=(const AbstractVectorGL&) = delete; - - /** @brief Move assignment */ - AbstractVectorGL& operator=(AbstractVectorGL&&) noexcept = default; - - /** @{ - * @name Texture binding - */ - - /** - * @brief Bind vector texture - * @return Reference to self (for method chaining) - * - * @see @ref DistanceFieldVectorGL::Flag::TextureTransformation, - * @ref VectorGL::Flag::TextureTransformation, - * @ref DistanceFieldVectorGL::setTextureMatrix(), - * @ref VectorGL::setTextureMatrix() - */ - AbstractVectorGL& bindVectorTexture(GL::Texture2D& texture); - - /** - * @} - */ - - #ifndef DOXYGEN_GENERATING_OUTPUT - protected: - #else - private: - #endif - /* Those textures are quite specific (and likely reused multiple times - per frame for e.g. text rendering, so put them in a specific slot. - Older iOS (and iOS WebGL) has only 8 texture units, so can't go - above that. Unit 7 is used by TextureTools::DistanceField. */ - enum: Int { VectorTextureUnit = 6 }; - - explicit AbstractVectorGL(NoCreateT) noexcept: GL::AbstractShaderProgram{NoCreate} {} - explicit AbstractVectorGL() = default; - ~AbstractVectorGL() = default; -}; - -/** -@brief Base for two-dimensional vector OpenGL shaders -@m_since_latest -*/ -typedef AbstractVectorGL<2> AbstractVectorGL2D; - -/** -@brief Base for three-dimensional vector OpenGL shader -@m_since_latest -*/ -typedef AbstractVectorGL<3> AbstractVectorGL3D; - -}} - -#endif diff --git a/src/Magnum/Shaders/CMakeLists.txt b/src/Magnum/Shaders/CMakeLists.txt index b5b55947b..e48a91475 100644 --- a/src/Magnum/Shaders/CMakeLists.txt +++ b/src/Magnum/Shaders/CMakeLists.txt @@ -31,7 +31,6 @@ corrade_add_resource(MagnumShaders_RESOURCES_GL resources-gl.conf) set_target_properties(MagnumShaders_RESOURCES_GL-dependencies PROPERTIES FOLDER "Magnum/Shaders") set(MagnumShaders_SRCS - AbstractVectorGL.cpp VertexColorGL.cpp ${MagnumShaders_RESOURCES_GL}) @@ -45,7 +44,6 @@ set(MagnumShaders_GracefulAssert_SRCS set(MagnumShaders_HEADERS DistanceFieldVectorGL.h - AbstractVectorGL.h FlatGL.h GenericGL.h MeshVisualizerGL.h @@ -59,7 +57,6 @@ set(MagnumShaders_HEADERS if(MAGNUM_BUILD_DEPRECATED) list(APPEND MagnumShaders_HEADERS DistanceFieldVector.h - AbstractVector.h Flat.h Generic.h MeshVisualizer.h diff --git a/src/Magnum/Shaders/DistanceFieldVector.frag b/src/Magnum/Shaders/DistanceFieldVector.frag index 51a79e78e..2f45fb27e 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.frag +++ b/src/Magnum/Shaders/DistanceFieldVector.frag @@ -66,7 +66,6 @@ uniform lowp float smoothness /* Textures */ #ifdef EXPLICIT_BINDING -/* See AbstractVector.h for details about the ID */ layout(binding = 6) #endif uniform lowp sampler2D vectorTexture; diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp index 1ce614bc3..022195230 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp @@ -32,6 +32,7 @@ #include "Magnum/GL/Context.h" #include "Magnum/GL/Extensions.h" #include "Magnum/GL/Shader.h" +#include "Magnum/GL/Texture.h" #include "Magnum/Math/Color.h" #include "Magnum/Math/Matrix3.h" #include "Magnum/Math/Matrix4.h" @@ -40,6 +41,10 @@ namespace Magnum { namespace Shaders { +namespace { + enum: Int { TextureUnit = 6 }; +} + template DistanceFieldVectorGL::DistanceFieldVectorGL(const Flags flags): _flags{flags} { #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ @@ -62,13 +67,13 @@ template DistanceFieldVectorGL::DistanceFiel vert.addSource(flags & Flag::TextureTransformation ? "#define TEXTURE_TRANSFORMATION\n" : "") .addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n" : "#define THREE_DIMENSIONS\n") .addSource(rs.get("generic.glsl")) - .addSource(rs.get("AbstractVector.vert")); + .addSource(rs.get("Vector.vert")); frag.addSource(rs.get("generic.glsl")) .addSource(rs.get("DistanceFieldVector.frag")); CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag})); - GL::AbstractShaderProgram::attachShaders({vert, frag}); + attachShaders({vert, frag}); /* ES3 has this done in the shader directly */ #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2) @@ -76,32 +81,31 @@ template DistanceFieldVectorGL::DistanceFiel if(!context.isExtensionSupported(version)) #endif { - GL::AbstractShaderProgram::bindAttributeLocation(AbstractVectorGL::Position::Location, "position"); - GL::AbstractShaderProgram::bindAttributeLocation(AbstractVectorGL::TextureCoordinates::Location, "textureCoordinates"); + bindAttributeLocation(Position::Location, "position"); + bindAttributeLocation(TextureCoordinates::Location, "textureCoordinates"); } #endif - CORRADE_INTERNAL_ASSERT_OUTPUT(GL::AbstractShaderProgram::link()); + CORRADE_INTERNAL_ASSERT_OUTPUT(link()); #ifndef MAGNUM_TARGET_GLES if(!context.isExtensionSupported(version)) #endif { - _transformationProjectionMatrixUniform = GL::AbstractShaderProgram::uniformLocation("transformationProjectionMatrix"); + _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); if(flags & Flag::TextureTransformation) - _textureMatrixUniform = GL::AbstractShaderProgram::uniformLocation("textureMatrix"); - _colorUniform = GL::AbstractShaderProgram::uniformLocation("color"); - _outlineColorUniform = GL::AbstractShaderProgram::uniformLocation("outlineColor"); - _outlineRangeUniform = GL::AbstractShaderProgram::uniformLocation("outlineRange"); - _smoothnessUniform = GL::AbstractShaderProgram::uniformLocation("smoothness"); + _textureMatrixUniform = uniformLocation("textureMatrix"); + _colorUniform = uniformLocation("color"); + _outlineColorUniform = uniformLocation("outlineColor"); + _outlineRangeUniform = uniformLocation("outlineRange"); + _smoothnessUniform = uniformLocation("smoothness"); } #ifndef MAGNUM_TARGET_GLES if(!context.isExtensionSupported(version)) #endif { - GL::AbstractShaderProgram::setUniform(GL::AbstractShaderProgram::uniformLocation("vectorTexture"), - AbstractVectorGL::VectorTextureUnit); + setUniform(uniformLocation("vectorTexture"), TextureUnit); } /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ @@ -116,34 +120,39 @@ template DistanceFieldVectorGL::DistanceFiel } template DistanceFieldVectorGL& DistanceFieldVectorGL::setTransformationProjectionMatrix(const MatrixTypeFor& matrix) { - GL::AbstractShaderProgram::setUniform(_transformationProjectionMatrixUniform, matrix); + setUniform(_transformationProjectionMatrixUniform, matrix); return *this; } template DistanceFieldVectorGL& DistanceFieldVectorGL::setTextureMatrix(const Matrix3& matrix) { CORRADE_ASSERT(_flags & Flag::TextureTransformation, "Shaders::DistanceFieldVectorGL::setTextureMatrix(): the shader was not created with texture transformation enabled", *this); - GL::AbstractShaderProgram::setUniform(_textureMatrixUniform, matrix); + setUniform(_textureMatrixUniform, matrix); return *this; } template DistanceFieldVectorGL& DistanceFieldVectorGL::setColor(const Color4& color) { - GL::AbstractShaderProgram::setUniform(_colorUniform, color); + setUniform(_colorUniform, color); return *this; } template DistanceFieldVectorGL& DistanceFieldVectorGL::setOutlineColor(const Color4& color) { - GL::AbstractShaderProgram::setUniform(_outlineColorUniform, color); + setUniform(_outlineColorUniform, color); return *this; } template DistanceFieldVectorGL& DistanceFieldVectorGL::setOutlineRange(Float start, Float end) { - GL::AbstractShaderProgram::setUniform(_outlineRangeUniform, Vector2(start, end)); + setUniform(_outlineRangeUniform, Vector2(start, end)); return *this; } template DistanceFieldVectorGL& DistanceFieldVectorGL::setSmoothness(Float value) { - GL::AbstractShaderProgram::setUniform(_smoothnessUniform, value); + setUniform(_smoothnessUniform, value); + return *this; +} + +template DistanceFieldVectorGL& DistanceFieldVectorGL::bindVectorTexture(GL::Texture2D& texture) { + texture.bind(TextureUnit); return *this; } diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.h b/src/Magnum/Shaders/DistanceFieldVectorGL.h index f4752b357..c039940fd 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.h +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.h @@ -31,7 +31,8 @@ */ #include "Magnum/DimensionTraits.h" -#include "Magnum/Shaders/AbstractVectorGL.h" +#include "Magnum/GL/AbstractShaderProgram.h" +#include "Magnum/Shaders/GenericGL.h" #include "Magnum/Shaders/visibility.h" namespace Magnum { namespace Shaders { @@ -48,9 +49,10 @@ namespace Implementation { @m_since_latest Renders vector graphics in a form of signed distance field. See -@ref TextureTools::DistanceField for more information. Note that the final -rendered outlook will greatly depend on radius of input distance field and -value passed to @ref setSmoothness(). You need to provide @ref Position and +@ref TextureTools::DistanceField for more information and @ref VectorGL for a +simpler variant of this shader. Note that the final rendered outlook will +greatly depend on radius of input distance field and value passed to +@ref setSmoothness(). You need to provide @ref Position and @ref TextureCoordinates attributes in your triangle mesh and call at least @ref bindVectorTexture(). By default, the shader renders the distance field texture with a white color in an identity transformation, use @@ -79,8 +81,34 @@ Common rendering setup: large zoom levels, make it optional as it might have negative performance impact */ -template class MAGNUM_SHADERS_EXPORT DistanceFieldVectorGL: public AbstractVectorGL { +template class MAGNUM_SHADERS_EXPORT DistanceFieldVectorGL: public GL::AbstractShaderProgram { public: + /** + * @brief Vertex position + * + * @ref shaders-generic "Generic attribute", + * @ref Magnum::Vector2 "Vector2" in 2D, @ref Magnum::Vector3 "Vector3" + * in 3D. + */ + typedef typename GenericGL::Position Position; + + /** + * @brief 2D texture coordinates + * + * @ref shaders-generic "Generic attribute", + * @ref Magnum::Vector2 "Vector2". + */ + typedef typename GenericGL::TextureCoordinates TextureCoordinates; + + enum: UnsignedInt { + /** + * Color shader output. @ref shaders-generic "Generic output", + * present always. Expects three- or four-component floating-point + * or normalized buffer attachment. + */ + ColorOutput = GenericGL::ColorOutput + }; + #ifdef DOXYGEN_GENERATING_OUTPUT /** * @brief Flag @@ -129,12 +157,7 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * However note that this is a low-level and a potentially dangerous * API, see the documentation of @ref NoCreate for alternatives. */ - explicit DistanceFieldVectorGL(NoCreateT) noexcept - /** @todoc remove workaround when doxygen is sane */ - #ifndef DOXYGEN_GENERATING_OUTPUT - : AbstractVectorGL{NoCreate} - #endif - {} + explicit DistanceFieldVectorGL(NoCreateT) noexcept: GL::AbstractShaderProgram{NoCreate} {} /** @brief Copying is not allowed */ DistanceFieldVectorGL(const DistanceFieldVectorGL&) = delete; @@ -226,13 +249,24 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * @} */ - #ifndef DOXYGEN_GENERATING_OUTPUT - /* Overloads to remove WTF-factor from method chaining order */ - DistanceFieldVectorGL& bindVectorTexture(GL::Texture2D& texture) { - AbstractVectorGL::bindVectorTexture(texture); - return *this; - } - #endif + /** @{ + * @name Texture binding + */ + + /** + * @brief Bind vector texture + * @return Reference to self (for method chaining) + * + * @see @ref DistanceFieldVectorGL::Flag::TextureTransformation, + * @ref VectorGL::Flag::TextureTransformation, + * @ref DistanceFieldVectorGL::setTextureMatrix(), + * @ref VectorGL::setTextureMatrix() + */ + DistanceFieldVectorGL& bindVectorTexture(GL::Texture2D& texture); + + /** + * @} + */ private: /* Prevent accidentally calling irrelevant functions */ diff --git a/src/Magnum/Shaders/Shaders.h b/src/Magnum/Shaders/Shaders.h index 5e353add8..2e2e31392 100644 --- a/src/Magnum/Shaders/Shaders.h +++ b/src/Magnum/Shaders/Shaders.h @@ -38,15 +38,6 @@ namespace Magnum { namespace Shaders { #ifndef DOXYGEN_GENERATING_OUTPUT -template class AbstractVectorGL; -typedef AbstractVectorGL<2> AbstractVectorGL2D; -typedef AbstractVectorGL<3> AbstractVectorGL3D; -#ifdef MAGNUM_BUILD_DEPRECATED -template using AbstractVector CORRADE_DEPRECATED_ALIAS("use AbstractVectorGL instead") = AbstractVectorGL; -typedef CORRADE_DEPRECATED("use AbstractVectorGL2D instead") AbstractVectorGL2D AbstractVector2D; -typedef CORRADE_DEPRECATED("use AbstractVectorGL3D instead") AbstractVectorGL3D AbstractVector3D; -#endif - template class DistanceFieldVectorGL; typedef DistanceFieldVectorGL<2> DistanceFieldVectorGL2D; typedef DistanceFieldVectorGL<3> DistanceFieldVectorGL3D; diff --git a/src/Magnum/Shaders/Vector.frag b/src/Magnum/Shaders/Vector.frag index ec5da58c5..058c38b09 100644 --- a/src/Magnum/Shaders/Vector.frag +++ b/src/Magnum/Shaders/Vector.frag @@ -46,7 +46,6 @@ uniform lowp vec4 color ; #ifdef EXPLICIT_BINDING -/* See AbstractVector.h for details about the ID */ layout(binding = 6) #endif uniform lowp sampler2D vectorTexture; diff --git a/src/Magnum/Shaders/AbstractVector.vert b/src/Magnum/Shaders/Vector.vert similarity index 100% rename from src/Magnum/Shaders/AbstractVector.vert rename to src/Magnum/Shaders/Vector.vert diff --git a/src/Magnum/Shaders/VectorGL.cpp b/src/Magnum/Shaders/VectorGL.cpp index db401d3f4..214f04ab6 100644 --- a/src/Magnum/Shaders/VectorGL.cpp +++ b/src/Magnum/Shaders/VectorGL.cpp @@ -32,6 +32,7 @@ #include "Magnum/GL/Context.h" #include "Magnum/GL/Extensions.h" #include "Magnum/GL/Shader.h" +#include "Magnum/GL/Texture.h" #include "Magnum/Math/Color.h" #include "Magnum/Math/Matrix3.h" #include "Magnum/Math/Matrix4.h" @@ -40,6 +41,10 @@ namespace Magnum { namespace Shaders { +namespace { + enum: Int { TextureUnit = 6 }; +} + template VectorGL::VectorGL(const Flags flags): _flags{flags} { #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ @@ -62,13 +67,13 @@ template VectorGL::VectorGL(const Flags flag vert.addSource(flags & Flag::TextureTransformation ? "#define TEXTURE_TRANSFORMATION\n" : "") .addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n" : "#define THREE_DIMENSIONS\n") .addSource(rs.get("generic.glsl")) - .addSource(rs.get("AbstractVector.vert")); + .addSource(rs.get("Vector.vert")); frag.addSource(rs.get("generic.glsl")) .addSource(rs.get("Vector.frag")); CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag})); - GL::AbstractShaderProgram::attachShaders({vert, frag}); + attachShaders({vert, frag}); /* ES3 has this done in the shader directly */ #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2) @@ -76,29 +81,29 @@ template VectorGL::VectorGL(const Flags flag if(!context.isExtensionSupported(version)) #endif { - GL::AbstractShaderProgram::bindAttributeLocation(AbstractVectorGL::Position::Location, "position"); - GL::AbstractShaderProgram::bindAttributeLocation(AbstractVectorGL::TextureCoordinates::Location, "textureCoordinates"); + bindAttributeLocation(Position::Location, "position"); + bindAttributeLocation(TextureCoordinates::Location, "textureCoordinates"); } #endif - CORRADE_INTERNAL_ASSERT_OUTPUT(GL::AbstractShaderProgram::link()); + CORRADE_INTERNAL_ASSERT_OUTPUT(link()); #ifndef MAGNUM_TARGET_GLES if(!context.isExtensionSupported(version)) #endif { - _transformationProjectionMatrixUniform = GL::AbstractShaderProgram::uniformLocation("transformationProjectionMatrix"); + _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); if(flags & Flag::TextureTransformation) - _textureMatrixUniform = GL::AbstractShaderProgram::uniformLocation("textureMatrix"); - _backgroundColorUniform = GL::AbstractShaderProgram::uniformLocation("backgroundColor"); - _colorUniform = GL::AbstractShaderProgram::uniformLocation("color"); + _textureMatrixUniform = uniformLocation("textureMatrix"); + _backgroundColorUniform = uniformLocation("backgroundColor"); + _colorUniform = uniformLocation("color"); } #ifndef MAGNUM_TARGET_GLES if(!context.isExtensionSupported(version)) #endif { - GL::AbstractShaderProgram::setUniform(GL::AbstractShaderProgram::uniformLocation("vectorTexture"), AbstractVectorGL::VectorTextureUnit); + setUniform(uniformLocation("vectorTexture"), TextureUnit); } /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ @@ -111,24 +116,29 @@ template VectorGL::VectorGL(const Flags flag } template VectorGL& VectorGL::setTransformationProjectionMatrix(const MatrixTypeFor& matrix) { - GL::AbstractShaderProgram::setUniform(_transformationProjectionMatrixUniform, matrix); + setUniform(_transformationProjectionMatrixUniform, matrix); return *this; } template VectorGL& VectorGL::setTextureMatrix(const Matrix3& matrix) { CORRADE_ASSERT(_flags & Flag::TextureTransformation, "Shaders::VectorGL::setTextureMatrix(): the shader was not created with texture transformation enabled", *this); - GL::AbstractShaderProgram::setUniform(_textureMatrixUniform, matrix); + setUniform(_textureMatrixUniform, matrix); return *this; } template VectorGL& VectorGL::setBackgroundColor(const Color4& color) { - GL::AbstractShaderProgram::setUniform(_backgroundColorUniform, color); + setUniform(_backgroundColorUniform, color); return *this; } template VectorGL& VectorGL::setColor(const Color4& color) { - GL::AbstractShaderProgram::setUniform(_colorUniform, color); + setUniform(_colorUniform, color); + return *this; +} + +template VectorGL& VectorGL::bindVectorTexture(GL::Texture2D& texture) { + texture.bind(TextureUnit); return *this; } diff --git a/src/Magnum/Shaders/VectorGL.h b/src/Magnum/Shaders/VectorGL.h index d788f18d3..dd5694fc4 100644 --- a/src/Magnum/Shaders/VectorGL.h +++ b/src/Magnum/Shaders/VectorGL.h @@ -31,7 +31,8 @@ */ #include "Magnum/DimensionTraits.h" -#include "Magnum/Shaders/AbstractVectorGL.h" +#include "Magnum/GL/AbstractShaderProgram.h" +#include "Magnum/Shaders/GenericGL.h" #include "Magnum/Shaders/visibility.h" namespace Magnum { namespace Shaders { @@ -75,8 +76,34 @@ Common rendering setup: @see @ref shaders, @ref VectorGL2D, @ref VectorGL3D */ -template class MAGNUM_SHADERS_EXPORT VectorGL: public AbstractVectorGL { +template class MAGNUM_SHADERS_EXPORT VectorGL: public GL::AbstractShaderProgram { public: + /** + * @brief Vertex position + * + * @ref shaders-generic "Generic attribute", + * @ref Magnum::Vector2 "Vector2" in 2D, @ref Magnum::Vector3 "Vector3" + * in 3D. + */ + typedef typename GenericGL::Position Position; + + /** + * @brief 2D texture coordinates + * + * @ref shaders-generic "Generic attribute", + * @ref Magnum::Vector2 "Vector2". + */ + typedef typename GenericGL::TextureCoordinates TextureCoordinates; + + enum: UnsignedInt { + /** + * Color shader output. @ref shaders-generic "Generic output", + * present always. Expects three- or four-component floating-point + * or normalized buffer attachment. + */ + ColorOutput = GenericGL::ColorOutput + }; + #ifdef DOXYGEN_GENERATING_OUTPUT /** * @brief Flag @@ -125,12 +152,7 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public Ab * However note that this is a low-level and a potentially dangerous * API, see the documentation of @ref NoCreate for alternatives. */ - explicit VectorGL(NoCreateT) noexcept - /** @todoc remove workaround when doxygen is sane */ - #ifndef DOXYGEN_GENERATING_OUTPUT - : AbstractVectorGL{NoCreate} - #endif - {} + explicit VectorGL(NoCreateT) noexcept: GL::AbstractShaderProgram{NoCreate} {} /** @brief Copying is not allowed */ VectorGL(const VectorGL&) = delete; @@ -195,13 +217,21 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public Ab * @} */ - #ifndef DOXYGEN_GENERATING_OUTPUT - /* Overloads to remove WTF-factor from method chaining order */ - VectorGL& bindVectorTexture(GL::Texture2D& texture) { - AbstractVectorGL::bindVectorTexture(texture); - return *this; - } - #endif + /** @{ + * @name Texture binding + */ + + /** + * @brief Bind vector texture + * @return Reference to self (for method chaining) + * + * @see @ref Flag::TextureTransformation, @ref setTextureMatrix()s + */ + VectorGL& bindVectorTexture(GL::Texture2D& texture); + + /** + * @} + */ private: /* Prevent accidentally calling irrelevant functions */ diff --git a/src/Magnum/Shaders/resources-gl.conf b/src/Magnum/Shaders/resources-gl.conf index f545425c9..57a1c346e 100644 --- a/src/Magnum/Shaders/resources-gl.conf +++ b/src/Magnum/Shaders/resources-gl.conf @@ -1,8 +1,5 @@ group=MagnumShadersGL -[file] -filename=AbstractVector.vert - [file] filename=Flat.vert @@ -30,6 +27,9 @@ filename=Phong.vert [file] filename=Phong.frag +[file] +filename=Vector.vert + [file] filename=Vector.frag diff --git a/src/Magnum/Text/Renderer.cpp b/src/Magnum/Text/Renderer.cpp index 088561041..e6dfd0762 100644 --- a/src/Magnum/Text/Renderer.cpp +++ b/src/Magnum/Text/Renderer.cpp @@ -33,7 +33,7 @@ #include "Magnum/GL/Extensions.h" #include "Magnum/GL/Mesh.h" #include "Magnum/Math/Functions.h" -#include "Magnum/Shaders/AbstractVectorGL.h" +#include "Magnum/Shaders/GenericGL.h" #include "Magnum/Text/AbstractFont.h" #include "Magnum/Text/GlyphCache.h" @@ -255,9 +255,9 @@ template std::tuple Renderer(r); mesh.addVertexBuffer(vertexBuffer, 0, - typename Shaders::AbstractVectorGL::Position( - Shaders::AbstractVectorGL::Position::Components::Two), - typename Shaders::AbstractVectorGL::TextureCoordinates()); + typename Shaders::GenericGL::Position( + Shaders::GenericGL::Position::Components::Two), + typename Shaders::GenericGL::TextureCoordinates()); return r; } @@ -320,8 +320,9 @@ AbstractRenderer::~AbstractRenderer() = default; template Renderer::Renderer(AbstractFont& font, const GlyphCache& cache, const Float size, const Alignment alignment): AbstractRenderer(font, cache, size, alignment) { /* Finalize mesh configuration */ _mesh.addVertexBuffer(_vertexBuffer, 0, - typename Shaders::AbstractVectorGL::Position(Shaders::AbstractVectorGL::Position::Components::Two), - typename Shaders::AbstractVectorGL::TextureCoordinates()); + typename Shaders::GenericGL::Position( + Shaders::GenericGL::Position::Components::Two), + typename Shaders::GenericGL::TextureCoordinates()); } void AbstractRenderer::reserve(const uint32_t glyphCount, const GL::BufferUsage vertexBufferUsage, const GL::BufferUsage indexBufferUsage) { diff --git a/src/Magnum/Text/Renderer.h b/src/Magnum/Text/Renderer.h index eb9edfdf9..8640ea982 100644 --- a/src/Magnum/Text/Renderer.h +++ b/src/Magnum/Text/Renderer.h @@ -202,7 +202,7 @@ asynchronous buffer updates. There is no similar extension in WebGL, thus plain (and slow) buffer updates are used there. @see @ref Renderer2D, @ref Renderer3D, @ref AbstractFont, - @ref Shaders::AbstractVectorGL + @ref Shaders::VectorGL, @ref Shaders::DistanceFieldVectorGL */ template class MAGNUM_TEXT_EXPORT Renderer: public AbstractRenderer { public: @@ -217,8 +217,9 @@ template class MAGNUM_TEXT_EXPORT Renderer: public Abstr * @param usage Usage of vertex and index buffer * @param alignment Text alignment * - * Returns mesh prepared for use with @ref Shaders::AbstractVectorGL - * subclasses and rectangle spanning the rendered text. + * Returns mesh prepared for use with @ref Shaders::VectorGL or + * @ref Shaders::DistanceFieldVectorGL and rectangle spanning the + * rendered text. */ static std::tuple render(AbstractFont& font, const GlyphCache& cache, Float size, const std::string& text, GL::Buffer& vertexBuffer, GL::Buffer& indexBuffer, GL::BufferUsage usage, Alignment alignment = Alignment::LineLeft); From 96a0ecd87ea4000ae0c6056eb769ac8120ac3759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 17 May 2021 17:16:41 +0200 Subject: [PATCH 09/93] Shaders: doc++ --- src/Magnum/Shaders/FlatGL.h | 19 +++++++++----- src/Magnum/Shaders/PhongGL.h | 48 ++++++++++++++++++++++------------ src/Magnum/Shaders/Vector.frag | 2 ++ 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/src/Magnum/Shaders/FlatGL.h b/src/Magnum/Shaders/FlatGL.h index cf08d8362..24db89982 100644 --- a/src/Magnum/Shaders/FlatGL.h +++ b/src/Magnum/Shaders/FlatGL.h @@ -290,8 +290,8 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: AlphaMask = 1 << 1, /** - * Multiply diffuse color with a vertex color. Requires either - * the @ref Color3 or @ref Color4 attribute to be present. + * Multiply the color with a vertex color. Requires either the + * @ref Color3 or @ref Color4 attribute to be present. * @m_since{2019,10} */ VertexColor = 1 << 2, @@ -424,7 +424,10 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * @brief Set transformation and projection matrix * @return Reference to self (for method chaining) * - * Initial value is an identity matrix. + * Initial value is an identity matrix. If + * @ref Flag::InstancedTransformation is set, the per-instance + * transformation matrix coming from the @ref TransformationMatrix + * attribute is applied first, before this one. */ FlatGL& setTransformationProjectionMatrix(const MatrixTypeFor& matrix); @@ -435,7 +438,9 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * * Expects that the shader was created with * @ref Flag::TextureTransformation enabled. Initial value is an - * identity matrix. + * identity matrix. If @ref Flag::InstancedTextureOffset is set, the + * per-instance offset coming from the @ref TextureOffset attribute is + * applied first, before this matrix. */ FlatGL& setTextureMatrix(const Matrix3& matrix); @@ -444,7 +449,9 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * @return Reference to self (for method chaining) * * Initial value is @cpp 0xffffffff_rgbaf @ce. If @ref Flag::Textured - * is set, the color will be multiplied with the texture. + * is set, the color is multiplied with the texture. If + * @ref Flag::VertexColor is set, the color is multiplied with a color + * coming from the @ref Color3 / @ref Color4 attribute. * @see @ref bindTexture() */ FlatGL& setColor(const Magnum::Color4& color); @@ -473,7 +480,7 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * enabled. Value set here is written to the @ref ObjectIdOutput, see * @ref Shaders-FlatGL-object-id for more information. Default is * @cpp 0 @ce. If @ref Flag::InstancedObjectId is enabled as well, this - * value is combined with ID coming from the @ref ObjectId attribute. + * value is added to the ID coming from the @ref ObjectId attribute. * @requires_gl30 Extension @gl_extension{EXT,gpu_shader4} * @requires_gles30 Object ID output requires integer support in * shaders, which is not available in OpenGL ES 2.0 or WebGL 1.0. diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index 177aeed60..0c52fa0c3 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -467,8 +467,9 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { AlphaMask = 1 << 3, /** - * Multiply diffuse color with a vertex color. Requires either - * the @ref Color3 or @ref Color4 attribute to be present. + * Multiply the diffuse and ambient color with a vertex color. + * Requires either the @ref Color3 or @ref Color4 attribute to be + * present. * @m_since{2019,10} */ VertexColor = 1 << 5, @@ -615,6 +616,8 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * If @ref Flag::AmbientTexture is set, default value is * @cpp 0xffffffff_rgbaf @ce and the color will be multiplied with * ambient texture, otherwise default value is @cpp 0x00000000_rgbaf @ce. + * If @ref Flag::VertexColor is set, the color is multiplied with a + * color coming from the @ref Color3 / @ref Color4 attribute. * @see @ref bindAmbientTexture(), @ref Shaders-PhongGL-lights-ambient */ PhongGL& setAmbientColor(const Magnum::Color4& color); @@ -623,9 +626,12 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @brief Set diffuse color * @return Reference to self (for method chaining) * - * Initial value is @cpp 0xffffffff_rgbaf @ce. If @ref lightCount() is - * zero, this function is a no-op, as diffuse color doesn't contribute - * to the output in that case. + * Initial value is @cpp 0xffffffff_rgbaf @ce. If + * @ref Flag::DiffuseTexture is set, the color will be multiplied with + * the texture. If @ref lightCount() is zero, this function is a no-op, + * as diffuse color doesn't contribute to the output in that case. + * If @ref Flag::VertexColor is set, the color is multiplied with a + * color coming from the @ref Color3 / @ref Color4 attribute. * @see @ref bindDiffuseTexture() */ PhongGL& setDiffuseColor(const Magnum::Color4& color); @@ -652,12 +658,12 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @brief Set specular color * @return Reference to self (for method chaining) * - * Initial value is @cpp 0xffffff00_rgbaf @ce. Color will be multiplied - * with specular texture if @ref Flag::SpecularTexture is set. If you - * want to have a fully diffuse material, set specular color to - * @cpp 0x00000000_rgbaf @ce. If @ref lightCount() is zero, this - * function is a no-op, as specular color doesn't contribute to the - * output in that case. + * Initial value is @cpp 0xffffff00_rgbaf @ce. If + * @ref Flag::SpecularTexture is set, the color will be multiplied with + * the texture. If you want to have a fully diffuse material, set + * the specular color to @cpp 0x00000000_rgbaf @ce. 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 bindSpecularTexture() */ PhongGL& setSpecularColor(const Magnum::Color4& color); @@ -696,7 +702,8 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * Expects that the shader was created with @ref Flag::ObjectId * enabled. Value set here is written to the @ref ObjectIdOutput, see * @ref Shaders-PhongGL-object-id for more information. Default is - * @cpp 0 @ce. + * @cpp 0 @ce. If @ref Flag::InstancedObjectId is enabled as well, this + * value is added to the ID coming from the @ref ObjectId attribute. * @requires_gl30 Extension @gl_extension{EXT,gpu_shader4} * @requires_gles30 Object ID output requires integer support in * shaders, which is not available in OpenGL ES 2.0 or WebGL 1.0. @@ -709,7 +716,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @return Reference to self (for method chaining) * * You need to set also @ref setNormalMatrix() with a corresponding - * value. Initial value is an identity matrix. + * value. Initial value is an identity matrix. If + * @ref Flag::InstancedTransformation is set, the per-instance + * transformation coming from the @ref TransformationMatrix attribute + * is applied first, before this one. */ PhongGL& setTransformationMatrix(const Matrix4& matrix); @@ -722,7 +732,9 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @ref setTransformationMatrix() with a corresponding value. Initial * value is an identity matrix. If @ref lightCount() is zero, this * function is a no-op, as normals don't contribute to the output in - * that case. + * that case. If @ref Flag::InstancedTransformation is set, the + * per-instance normal matrix coming from the @ref NormalMatrix + * attribute is applied first, before this one. * @see @ref Math::Matrix4::normalMatrix() */ PhongGL& setNormalMatrix(const Matrix3x3& matrix); @@ -744,7 +756,9 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * * Expects that the shader was created with * @ref Flag::TextureTransformation enabled. Initial value is an - * identity matrix. + * identity matrix. If @ref Flag::InstancedTextureOffset is set, the + * per-instance offset coming from the @ref TextureOffset atttribute is + * applied first, before this matrix. */ PhongGL& setTextureMatrix(const Matrix3& matrix); @@ -754,8 +768,8 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @m_since_latest * * Depending on the fourth component, the value is treated as either a - *camera-relative position of a point light, if the fourth component is - * @cpp 1.0f @ce; or a direction *to* a directional light, if the + * camera-relative position of a point light, if the fourth component + * is @cpp 1.0f @ce; or a direction *to* a directional light, if the * fourth component is @cpp 0.0f @ce. Expects that the size of the * @p positions array is the same as @ref lightCount(). Initial values * are @cpp {0.0f, 0.0f, 1.0f, 0.0f} @ce --- a directional "fill" light diff --git a/src/Magnum/Shaders/Vector.frag b/src/Magnum/Shaders/Vector.frag index 058c38b09..8f0f63795 100644 --- a/src/Magnum/Shaders/Vector.frag +++ b/src/Magnum/Shaders/Vector.frag @@ -45,6 +45,8 @@ uniform lowp vec4 color #endif ; +/* Textures */ + #ifdef EXPLICIT_BINDING layout(binding = 6) #endif From ae3fd92fd307c77d85dbb01d655da5cf795b1cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 17 May 2021 19:17:47 +0200 Subject: [PATCH 10/93] Shaders: uh oh? good thing there was no bug hidden underneath. --- src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp index e9aeaee0a..ba84c7d4a 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp @@ -349,7 +349,7 @@ void DistanceFieldVectorGLTest::renderDefaults3D() { .setSubImage(0, {}, *image); #endif - DistanceFieldVectorGL2D{} + DistanceFieldVectorGL3D{} .bindVectorTexture(texture) .draw(plane); From 52c6c30fdf067c41284f89bf8bf895b4dc25174c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 17 May 2021 22:45:17 +0200 Subject: [PATCH 11/93] Shaders: this SKIP was too early. --- src/Magnum/Shaders/Test/VertexColorGLTest.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp index d0e73cbd6..576698c78 100644 --- a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp @@ -311,10 +311,6 @@ template void VertexColorGLTest::render2D() { template void VertexColorGLTest::render3D() { setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); - if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || - !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) - CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); - Trade::MeshData sphereData = Primitives::uvSphereSolid(16, 32, Primitives::UVSphereFlag::TextureCoordinates); @@ -338,6 +334,10 @@ template void VertexColorGLTest::render3D() { MAGNUM_VERIFY_NO_GL_ERROR(); + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) /* AMD has one different pixel compared to Intel, SwiftShader has differently rasterized edges on five pixels. Apple A8 some more. */ From 1279d3fe5d14744e7e70af727b3421ad216590ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 18 May 2021 11:57:52 +0200 Subject: [PATCH 12/93] Shaders: merge all MeshVisualizer constructor tests together. It didn't really make sense to have a separate test case just to check a bunch of extra extensions first. This makes it much easier to test the UBO variants as well. Plus the "invalid" tests don't actually need to test any extensions, as they're supposed to fail before any extension-dependent code path is called. --- .../Shaders/Test/MeshVisualizerGLTest.cpp | 178 +++++------------- 1 file changed, 50 insertions(+), 128 deletions(-) diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index 183d504f4..30d553444 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -71,17 +71,8 @@ struct MeshVisualizerGLTest: GL::OpenGLTester { void construct2D(); void construct3D(); - #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - void constructWireframeGeometryShader2D(); - void constructGeometryShader3D(); - #endif - void construct2DInvalid(); void construct3DInvalid(); - #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - void construct3DGeometryShaderDisabledButNeeded(); - void construct3DConflictingBitangentInput(); - #endif void constructMove2D(); void constructMove3D(); @@ -155,6 +146,9 @@ constexpr struct { const char* name; MeshVisualizerGL2D::Flags flags; } ConstructData2D[] { + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + {"wireframe", MeshVisualizerGL2D::Flag::Wireframe}, + #endif {"wireframe w/o GS", MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader}, #ifndef MAGNUM_TARGET_GLES2 {"object ID", MeshVisualizerGL2D::Flag::InstancedObjectId}, @@ -170,6 +164,9 @@ constexpr struct { const char* name; MeshVisualizerGL3D::Flags flags; } ConstructData3D[] { + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + {"wireframe", MeshVisualizerGL3D::Flag::Wireframe}, + #endif {"wireframe w/o GS", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader}, #ifndef MAGNUM_TARGET_GLES2 {"object ID", MeshVisualizerGL3D::Flag::InstancedObjectId}, @@ -177,16 +174,9 @@ constexpr struct { #ifndef MAGNUM_TARGET_WEBGL {"primitive ID", MeshVisualizerGL3D::Flag::PrimitiveId}, #endif - {"primitive ID from vertex ID", MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId} + {"primitive ID from vertex ID", MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId}, #endif -}; - -#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) -constexpr struct { - const char* name; - MeshVisualizerGL3D::Flags flags; -} ConstructGeometryShaderData3D[] { - {"wireframe", MeshVisualizerGL3D::Flag::Wireframe}, + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) {"tangent direction", MeshVisualizerGL3D::Flag::TangentDirection}, {"bitangent direction from tangent", MeshVisualizerGL3D::Flag::BitangentFromTangentDirection}, {"bitangent direction", MeshVisualizerGL3D::Flag::BitangentDirection}, @@ -197,8 +187,8 @@ constexpr struct { {"wireframe + t/n direction", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::NormalDirection}, {"wireframe + object id + t/n direction", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::NormalDirection}, {"wireframe + vertex id + t/b direction", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::VertexId|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentDirection} + #endif }; -#endif constexpr struct { const char* name; @@ -242,7 +232,15 @@ constexpr struct { ": Flag::InstancedObjectId, Flag::VertexId and Flag::PrimitiveId are mutually exclusive"}, {"both vertex and primitive id", MeshVisualizerGL3D::Flag::VertexId|MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId, - ": Flag::InstancedObjectId, Flag::VertexId and Flag::PrimitiveId are mutually exclusive"} + ": Flag::InstancedObjectId, Flag::VertexId and Flag::PrimitiveId are mutually exclusive"}, + #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + {"geometry shader disabled but needed", + MeshVisualizerGL3D::Flag::NoGeometryShader|MeshVisualizerGL3D::Flag::NormalDirection, + "3D: geometry shader has to be enabled when rendering TBN direction"}, + {"conflicting bitangent input", + MeshVisualizerGL3D::Flag::BitangentFromTangentDirection|MeshVisualizerGL3D::Flag::BitangentDirection, + "3D: Flag::BitangentDirection and Flag::BitangentFromTangentDirection are mutually exclusive"} #endif }; @@ -410,25 +408,12 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { addInstancedTests({&MeshVisualizerGLTest::construct3D}, Containers::arraySize(ConstructData3D)); - #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - addTests({&MeshVisualizerGLTest::constructWireframeGeometryShader2D}); - - addInstancedTests({&MeshVisualizerGLTest::constructGeometryShader3D}, - Containers::arraySize(ConstructGeometryShaderData3D)); - #endif - addInstancedTests({&MeshVisualizerGLTest::construct2DInvalid}, Containers::arraySize(ConstructInvalidData2D)); addInstancedTests({&MeshVisualizerGLTest::construct3DInvalid}, Containers::arraySize(ConstructInvalidData3D)); - addTests({ - #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - &MeshVisualizerGLTest::construct3DGeometryShaderDisabledButNeeded, - &MeshVisualizerGLTest::construct3DConflictingBitangentInput, - #endif - - &MeshVisualizerGLTest::constructMove2D, + addTests({&MeshVisualizerGLTest::constructMove2D, &MeshVisualizerGLTest::constructMove3D, &MeshVisualizerGLTest::setWireframeNotEnabled2D, @@ -567,6 +552,23 @@ void MeshVisualizerGLTest::construct2D() { ) CORRADE_SKIP("gl_PrimitiveID not supported."); #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if((data.flags & MeshVisualizerGL2D::Flag::Wireframe) && !(data.flags & MeshVisualizerGL2D::Flag::NoGeometryShader)) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() << "is not supported."); + #endif + + #ifdef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + CORRADE_INFO("Using" << GL::Extensions::NV::shader_noperspective_interpolation::string()); + #endif + } + #endif + MeshVisualizerGL2D shader{data.flags}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_VERIFY(shader.id()); @@ -609,73 +611,35 @@ void MeshVisualizerGLTest::construct3D() { ) CORRADE_SKIP("gl_PrimitiveID not supported."); #endif - MeshVisualizerGL3D shader{data.flags}; - CORRADE_COMPARE(shader.flags(), data.flags); - CORRADE_VERIFY(shader.id()); - { - #ifdef CORRADE_TARGET_APPLE - CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(((data.flags & MeshVisualizerGL3D::Flag::Wireframe) && !(data.flags & MeshVisualizerGL3D::Flag::NoGeometryShader)) || (data.flags & (MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentDirection|MeshVisualizerGL3D::Flag::BitangentFromTangentDirection|MeshVisualizerGL3D::Flag::NormalDirection))) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() << "is not supported."); #endif - CORRADE_VERIFY(shader.validate().first); - } - - MAGNUM_VERIFY_NO_GL_ERROR(); -} - -#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) -void MeshVisualizerGLTest::constructWireframeGeometryShader2D() { - #ifndef MAGNUM_TARGET_GLES - if(!GL::Context::current().isExtensionSupported()) - CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() << "is not supported."); - #else - if(!GL::Context::current().isExtensionSupported()) - CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() << "is not supported."); - #endif - #ifdef MAGNUM_TARGET_GLES - if(GL::Context::current().isExtensionSupported()) - Debug() << "Using" << GL::Extensions::NV::shader_noperspective_interpolation::string(); - #endif - - MeshVisualizerGL2D shader{MeshVisualizerGL2D::Flag::Wireframe}; - CORRADE_COMPARE(shader.flags(), MeshVisualizerGL2D::Flag::Wireframe); - { - #ifdef CORRADE_TARGET_APPLE - CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); + #ifdef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + CORRADE_INFO("Using" << GL::Extensions::NV::shader_noperspective_interpolation::string()); #endif - CORRADE_VERIFY(shader.id()); - CORRADE_VERIFY(shader.validate().first); } -} - -void MeshVisualizerGLTest::constructGeometryShader3D() { - auto&& data = ConstructGeometryShaderData3D[testCaseInstanceId()]; - setTestCaseDescription(data.name); - - #ifndef MAGNUM_TARGET_GLES - if(!GL::Context::current().isExtensionSupported()) - CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() << "is not supported."); - #else - if(!GL::Context::current().isExtensionSupported()) - CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() << "is not supported."); - #endif - - #ifdef MAGNUM_TARGET_GLES - if(GL::Context::current().isExtensionSupported()) - Debug() << "Using" << GL::Extensions::NV::shader_noperspective_interpolation::string(); #endif MeshVisualizerGL3D shader{data.flags}; CORRADE_COMPARE(shader.flags(), data.flags); + CORRADE_VERIFY(shader.id()); { #ifdef CORRADE_TARGET_APPLE CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); #endif - CORRADE_VERIFY(shader.id()); CORRADE_VERIFY(shader.validate().first); } + + MAGNUM_VERIFY_NO_GL_ERROR(); } -#endif void MeshVisualizerGLTest::construct2DInvalid() { auto&& data = ConstructInvalidData2D[testCaseInstanceId()]; @@ -705,48 +669,6 @@ void MeshVisualizerGLTest::construct3DInvalid() { CORRADE_COMPARE(out.str(), Utility::formatString("Shaders::MeshVisualizerGL{}\n", data.message)); } -#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) -void MeshVisualizerGLTest::construct3DGeometryShaderDisabledButNeeded() { - #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::ARB::geometry_shader4::string() << "is not supported."); - #else - if(!GL::Context::current().isExtensionSupported()) - CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() << "is not supported."); - #endif - - std::ostringstream out; - Error redirectError{&out}; - MeshVisualizerGL3D{MeshVisualizerGL3D::Flag::NoGeometryShader|MeshVisualizerGL3D::Flag::NormalDirection}; - CORRADE_COMPARE(out.str(), - "Shaders::MeshVisualizerGL3D: geometry shader has to be enabled when rendering TBN direction\n"); -} - -void MeshVisualizerGLTest::construct3DConflictingBitangentInput() { - #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::ARB::geometry_shader4::string() << "is not supported."); - #else - if(!GL::Context::current().isExtensionSupported()) - CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() << "is not supported."); - #endif - - std::ostringstream out; - Error redirectError{&out}; - MeshVisualizerGL3D{MeshVisualizerGL3D::Flag::BitangentFromTangentDirection|MeshVisualizerGL3D::Flag::BitangentDirection}; - CORRADE_COMPARE(out.str(), - "Shaders::MeshVisualizerGL3D: Flag::BitangentDirection and Flag::BitangentFromTangentDirection are mutually exclusive\n"); -} -#endif - void MeshVisualizerGLTest::constructMove2D() { MeshVisualizerGL2D a{MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader}; const GLuint id = a.id(); From 9bf1d4c1bd778ff4b49ac1de9cc4ceb846e94593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 18 May 2021 22:00:59 +0200 Subject: [PATCH 13/93] Shaders: whoops, tested with a wrong shader here as well. But the output is the same because the default 3D projection is orthographic. --- src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index 30d553444..92f0c2f4a 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -1109,7 +1109,7 @@ void MeshVisualizerGLTest::renderDefaultsVertexId3D() { if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP("gl_VertexID not supported"); - MeshVisualizerGL2D{MeshVisualizerGL2D::Flag::VertexId} + MeshVisualizerGL3D{MeshVisualizerGL3D::Flag::VertexId} .bindColorMapTexture(_colorMapTexture) .draw(MeshTools::compile(Primitives::icosphereSolid(0))); @@ -1121,6 +1121,7 @@ void MeshVisualizerGLTest::renderDefaultsVertexId3D() { Utility::Directory::join(_testDir, "MeshVisualizerTestFiles/defaults-vertexid3D.tga"), (DebugTools::CompareImageToFile{_manager, 1.0f, 0.012f})); } + void MeshVisualizerGLTest::renderDefaultsPrimitiveId2D() { if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) @@ -1191,9 +1192,9 @@ void MeshVisualizerGLTest::renderDefaultsPrimitiveId3D() { ) CORRADE_SKIP("gl_VertexID not supported."); #endif - MeshVisualizerGL2D::Flags flags; + MeshVisualizerGL3D::Flags flags; #ifdef MAGNUM_TARGET_WEBGL - flags = MeshVisualizerGL2D::Flag::PrimitiveIdFromVertexId; + flags = MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId; #else #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isVersionSupported(GL::Version::GL320)) @@ -1202,16 +1203,16 @@ void MeshVisualizerGLTest::renderDefaultsPrimitiveId3D() { #endif { Debug{} << "Using primitive ID from vertex ID"; - flags = MeshVisualizerGL2D::Flag::PrimitiveIdFromVertexId; + flags = MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId; } - else flags = MeshVisualizerGL2D::Flag::PrimitiveId; + else flags = MeshVisualizerGL3D::Flag::PrimitiveId; #endif Trade::MeshData icosphereData = Primitives::icosphereSolid(0); - if(flags >= MeshVisualizerGL2D::Flag::PrimitiveIdFromVertexId) + if(flags >= MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId) icosphereData = MeshTools::duplicate(icosphereData); - MeshVisualizerGL2D{flags} + MeshVisualizerGL3D{flags} .bindColorMapTexture(_colorMapTexture) .draw(MeshTools::compile(icosphereData)); From d5623b03864ad5e6d63ffd0ecbb1d1e198c54dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 18 May 2021 22:55:42 +0200 Subject: [PATCH 14/93] Shaders: wrongly copypasted variable name in a test. --- src/Magnum/Shaders/MeshVisualizer.geom | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Shaders/MeshVisualizer.geom b/src/Magnum/Shaders/MeshVisualizer.geom index e5b65c426..e609692d2 100644 --- a/src/Magnum/Shaders/MeshVisualizer.geom +++ b/src/Magnum/Shaders/MeshVisualizer.geom @@ -41,7 +41,6 @@ layout(location = 5) #endif uniform lowp vec2 viewportSize; /* defaults to zero */ -layout(triangles) in; #if (defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION)) && (defined(WIREFRAME_RENDERING) || defined(INSTANCED_OBJECT_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID)) #ifdef EXPLICIT_UNIFORM_LOCATION @@ -85,6 +84,8 @@ uniform lowp float smoothness /* Inputs */ +layout(triangles) in; + #ifdef TANGENT_DIRECTION in highp vec4 tangentEndpoint[]; #endif From 87b7be26f9c8070e71de10945c4ce3de59e86ae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 20 May 2021 12:19:46 +0200 Subject: [PATCH 15/93] Shaders: forgot to rename this. --- src/Magnum/Shaders/DistanceFieldVectorGL.h | 4 ++-- src/Magnum/Shaders/MeshVisualizerGL.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.h b/src/Magnum/Shaders/DistanceFieldVectorGL.h index c039940fd..538cb8961 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.h +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.h @@ -300,10 +300,10 @@ typedef DistanceFieldVectorGL<3> DistanceFieldVectorGL3D; #ifdef DOXYGEN_GENERATING_OUTPUT /** @debugoperatorclassenum{DistanceFieldVectorGL,DistanceFieldVectorGL::Flag} */ -template Debug& operator<<(Debug& debug, DistanceFieldVector::Flag value); +template Debug& operator<<(Debug& debug, DistanceFieldVectorGL::Flag value); /** @debugoperatorclassenum{DistanceFieldVectorGL,DistanceFieldVectorGL::Flags} */ -template Debug& operator<<(Debug& debug, DistanceFieldVector::Flags value); +template Debug& operator<<(Debug& debug, DistanceFieldVectorGL::Flags value); #else namespace Implementation { MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, DistanceFieldVectorGLFlag value); diff --git a/src/Magnum/Shaders/MeshVisualizerGL.cpp b/src/Magnum/Shaders/MeshVisualizerGL.cpp index ef34527af..9fd3c2398 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.cpp +++ b/src/Magnum/Shaders/MeshVisualizerGL.cpp @@ -100,11 +100,11 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra #ifndef MAGNUM_TARGET_GLES const GL::Version version = context.supportedVersion({GL::Version::GL320, GL::Version::GL310, GL::Version::GL300, GL::Version::GL210}); - /* Extended in MeshVisualizer3D for TBN visualization */ + /* Extended in MeshVisualizerGL3D for TBN visualization */ CORRADE_INTERNAL_ASSERT(!(_flags & FlagBase::Wireframe) || _flags & FlagBase::NoGeometryShader || version >= GL::Version::GL320); #elif !defined(MAGNUM_TARGET_WEBGL) const GL::Version version = context.supportedVersion({GL::Version::GLES310, GL::Version::GLES300, GL::Version::GLES200}); - /* Extended in MeshVisualizer3D for TBN visualization */ + /* Extended in MeshVisualizerGL3D for TBN visualization */ CORRADE_INTERNAL_ASSERT(!(_flags & FlagBase::Wireframe) || _flags & FlagBase::NoGeometryShader || version >= GL::Version::GLES310); #else const GL::Version version = context.supportedVersion({GL::Version::GLES300, GL::Version::GLES200}); From 1e6da607c9adb40d66bca031791fdbad9b22aac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 20 May 2021 12:29:17 +0200 Subject: [PATCH 16/93] Shaders: minor. --- src/Magnum/Shaders/PhongGL.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index 0c52fa0c3..66547b5cd 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -1060,9 +1060,9 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { _shininessUniform{7}, _normalTextureScaleUniform{8}, _alphaMaskUniform{9}; - #ifndef MAGNUM_TARGET_GLES2 - Int _objectIdUniform{10}; - #endif + #ifndef MAGNUM_TARGET_GLES2 + Int _objectIdUniform{10}; + #endif Int _lightPositionsUniform{11}, _lightColorsUniform, /* 11 + lightCount, set in the constructor */ _lightSpecularColorsUniform, /* 11 + 2*lightCount */ From d343be1d8cbe8849e0f5d5874227e0c03732e889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 20 May 2021 12:33:38 +0200 Subject: [PATCH 17/93] Shaders: this accidentally didn't actually test the thing. ...but was the same as the "blending" case above. Fortunately there was no bug hidden underneath. --- src/Magnum/Shaders/Test/FlatGLTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index 34f2ff8f2..e6bfa3207 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -184,7 +184,7 @@ const struct { {"blending", "FlatTestFiles/textured2D-alpha.tga", "FlatTestFiles/textured3D-alpha.tga", true, FlatGL2D::Flag::Textured, 0.0f}, {"masking 0.0", "FlatTestFiles/textured2D.tga", "FlatTestFiles/textured3D.tga", false, - FlatGL2D::Flag::Textured, 0.0f}, + FlatGL2D::Flag::Textured|FlatGL2D::Flag::AlphaMask, 0.0f}, {"masking 0.5", "FlatTestFiles/textured2D-alpha-mask0.5.tga", "FlatTestFiles/textured3D-alpha-mask0.5.tga", false, FlatGL2D::Flag::Textured|FlatGL2D::Flag::AlphaMask, 0.5f}, {"masking 1.0", "TestFiles/alpha-mask1.0.tga", "TestFiles/alpha-mask1.0.tga", false, From 18dcfae51e469403e615eaf3f1818004255afa19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 20 May 2021 12:13:15 +0200 Subject: [PATCH 18/93] package/archlinux: don't run benchmarks in check(). Takes too long and is useless, as we don't see the output anyway. Also deduplicate the env var setters and export them just once at the start. --- package/archlinux/PKGBUILD | 20 +++++++++++--------- package/archlinux/PKGBUILD-coverage | 20 +++++++++++--------- package/archlinux/PKGBUILD-release | 20 +++++++++++--------- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index 4a962d6b5..7dba2f6c7 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -65,20 +65,22 @@ build() { check() { cd "$_rootdir/build" - CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 - MAGNUM_DISABLE_EXTENSIONS="GL_ARB_invalidate_subdata GL_ARB_multi_bind GL_ARB_robustness GL_ARB_separate_shader_objects GL_ARB_texture_storage GL_ARB_texture_storage_multisample GL_ARB_shading_language_420pack GL_ARB_explicit_uniform_location GL_ARB_explicit_attrib_location GL_ARB_texture_filter_anisotropic" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest - MAGNUM_DISABLE_EXTENSIONS="GL_ARB_direct_state_access" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest - MAGNUM_DISABLE_EXTENSIONS="GL_ARB_get_texture_sub_image" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest - MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest - MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest + export CORRADE_TEST_SKIP_BENCHMARKS=ON + export CORRADE_TEST_COLOR=ON + ctest --output-on-failure -j5 + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_invalidate_subdata GL_ARB_multi_bind GL_ARB_robustness GL_ARB_separate_shader_objects GL_ARB_texture_storage GL_ARB_texture_storage_multisample GL_ARB_shading_language_420pack GL_ARB_explicit_uniform_location GL_ARB_explicit_attrib_location GL_ARB_texture_filter_anisotropic" ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_direct_state_access" ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_get_texture_sub_image" ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" ctest --output-on-failure -j5 -R GLTest # Run all Vulkan tests with SwiftShader as well # Keep in sync with PKGBUILD-coverage, PKGBUILD-release and # package/ci/unix-desktop-vulkan.sh - MAGNUM_DEVICE=cpu CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + MAGNUM_DEVICE=cpu ctest --output-on-failure -j5 -R VkTest for device in "" cpu; do - MAGNUM_DEVICE=$device MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest - MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2 VK_KHR_bind_memory2 VK_KHR_create_renderpass2 VK_KHR_copy_commands2 VK_KHR_maintenance1 VK_KHR_multiview VK_KHR_maintenance2" MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + MAGNUM_DEVICE=$device MAGNUM_VULKAN_VERSION=1.0 ctest --output-on-failure -j5 -R VkTest + MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2 VK_KHR_bind_memory2 VK_KHR_create_renderpass2 VK_KHR_copy_commands2 VK_KHR_maintenance1 VK_KHR_multiview VK_KHR_maintenance2" MAGNUM_VULKAN_VERSION=1.0 ctest --output-on-failure -j5 -R VkTest done } diff --git a/package/archlinux/PKGBUILD-coverage b/package/archlinux/PKGBUILD-coverage index 43d54ff3c..89d33001c 100644 --- a/package/archlinux/PKGBUILD-coverage +++ b/package/archlinux/PKGBUILD-coverage @@ -65,21 +65,23 @@ build() { check() { cd "$_rootdir/build-coverage" - CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 || true + export CORRADE_TEST_SKIP_BENCHMARKS=ON + export CORRADE_TEST_COLOR=ON + ctest --output-on-failure -j5 || true - MAGNUM_DISABLE_EXTENSIONS="GL_ARB_invalidate_subdata GL_ARB_multi_bind GL_ARB_separate_shader_objects GL_ARB_texture_storage GL_ARB_texture_storage_multisample GL_ARB_texture_multisample GL_ARB_shading_language_420pack GL_ARB_explicit_uniform_location GL_ARB_explicit_attrib_location GL_ARB_texture_filter_anisotropic" CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest || true - MAGNUM_DISABLE_EXTENSIONS="GL_ARB_direct_state_access GL_ARB_robustness GL_ARB_multi_bind" CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest || true - MAGNUM_DISABLE_EXTENSIONS="GL_ARB_get_texture_sub_image" CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest || true - MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest || true - MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest || true + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_invalidate_subdata GL_ARB_multi_bind GL_ARB_separate_shader_objects GL_ARB_texture_storage GL_ARB_texture_storage_multisample GL_ARB_texture_multisample GL_ARB_shading_language_420pack GL_ARB_explicit_uniform_location GL_ARB_explicit_attrib_location GL_ARB_texture_filter_anisotropic" ctest --output-on-failure -j5 -R GLTest || true + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_direct_state_access GL_ARB_robustness GL_ARB_multi_bind" ctest --output-on-failure -j5 -R GLTest || true + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_get_texture_sub_image" ctest --output-on-failure -j5 -R GLTest || true + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" ctest --output-on-failure -j5 -R GLTest || true + MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" ctest --output-on-failure -j5 -R GLTest || true # Run all Vulkan tests with SwiftShader as well # Keep in sync with PKGBUILD, PKGBUILD-release and # package/ci/unix-desktop-vulkan.sh - MAGNUM_DEVICE=cpu CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true + MAGNUM_DEVICE=cpu ctest --output-on-failure -j5 -R VkTest || true for device in "" cpu; do - MAGNUM_DEVICE=$device MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true - MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2 VK_KHR_bind_memory2 VK_KHR_create_renderpass2 VK_KHR_copy_commands2 VK_KHR_maintenance1 VK_KHR_multiview VK_KHR_maintenance2" MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true + MAGNUM_DEVICE=$device MAGNUM_VULKAN_VERSION=1.0 ctest --output-on-failure -j5 -R VkTest || true + MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2 VK_KHR_bind_memory2 VK_KHR_create_renderpass2 VK_KHR_copy_commands2 VK_KHR_maintenance1 VK_KHR_multiview VK_KHR_maintenance2" MAGNUM_VULKAN_VERSION=1.0 ctest --output-on-failure -j5 -R VkTest || true done ./Debug/bin/magnum-al-info > /dev/null diff --git a/package/archlinux/PKGBUILD-release b/package/archlinux/PKGBUILD-release index f3ff212c5..7c6364886 100644 --- a/package/archlinux/PKGBUILD-release +++ b/package/archlinux/PKGBUILD-release @@ -101,22 +101,24 @@ build() { } check() { + export CORRADE_TEST_SKIP_BENCHMARKS=ON + export CORRADE_TEST_COLOR=ON for i in build build-release; do cd "$_rootdir/$i" - CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 - MAGNUM_DISABLE_EXTENSIONS="GL_ARB_invalidate_subdata GL_ARB_multi_bind GL_ARB_robustness GL_ARB_separate_shader_objects GL_ARB_texture_storage GL_ARB_texture_storage_multisample GL_ARB_shading_language_420pack GL_ARB_explicit_uniform_location GL_ARB_explicit_attrib_location GL_ARB_texture_filter_anisotropic" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest - MAGNUM_DISABLE_EXTENSIONS="GL_ARB_direct_state_access" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest - MAGNUM_DISABLE_EXTENSIONS="GL_ARB_get_texture_sub_image" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest - MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest - MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest + ctest --output-on-failure -j5 + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_invalidate_subdata GL_ARB_multi_bind GL_ARB_robustness GL_ARB_separate_shader_objects GL_ARB_texture_storage GL_ARB_texture_storage_multisample GL_ARB_shading_language_420pack GL_ARB_explicit_uniform_location GL_ARB_explicit_attrib_location GL_ARB_texture_filter_anisotropic" ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_direct_state_access" ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_get_texture_sub_image" ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" ctest --output-on-failure -j5 -R GLTest # Run all Vulkan tests with SwiftShader as well # Keep in sync with PKGBUILD, PKGBUILD-coverage and # package/ci/unix-desktop-vulkan.sh - MAGNUM_DEVICE=cpu CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + MAGNUM_DEVICE=cpu ctest --output-on-failure -j5 -R VkTest for device in "" cpu; do - MAGNUM_DEVICE=$device MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest - MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2 VK_KHR_bind_memory2 VK_KHR_create_renderpass2 VK_KHR_copy_commands2 VK_KHR_maintenance1 VK_KHR_multiview VK_KHR_maintenance2" MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + MAGNUM_DEVICE=$device MAGNUM_VULKAN_VERSION=1.0 ctest --output-on-failure -j5 -R VkTest + MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2 VK_KHR_bind_memory2 VK_KHR_create_renderpass2 VK_KHR_copy_commands2 VK_KHR_maintenance1 VK_KHR_multiview VK_KHR_maintenance2" MAGNUM_VULKAN_VERSION=1.0 ctest --output-on-failure -j5 -R VkTest done done } From 666fe1760376094a47a41cdca6cc1c3ee439a5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 8 Jun 2021 15:49:52 +0200 Subject: [PATCH 19/93] package/ci: don't even attempt to run benchmarks on SwiftShader. The damn thing has more bugs than features and doesn't support timer queries anyway, so there's no point in trying anything. --- package/ci/unix-desktop-gles.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/package/ci/unix-desktop-gles.sh b/package/ci/unix-desktop-gles.sh index 80a153493..f709f19b1 100755 --- a/package/ci/unix-desktop-gles.sh +++ b/package/ci/unix-desktop-gles.sh @@ -56,6 +56,12 @@ cmake .. \ -DBUILD_GL_TESTS=ON \ -G Ninja ninja $NINJA_JOBS + +# Don't run any benchmarks. SwiftShader doesn't support +# EXT_disjoint_timer_query anyway and the CPU-side things are run in the usual +# desktop build already. +export CORRADE_TEST_SKIP_BENCHMARKS=ON + CORRADE_TEST_COLOR=ON ctest -V MAGNUM_DISABLE_EXTENSIONS="GL_OES_vertex_array_object GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_OES_vertex_array_object GL_NV_framebuffer_multisample GL_NV_framebuffer_blit GL_EXT_robustness GL_EXT_draw_elements_base_vertex GL_OES_draw_elements_base_vertex GL_ANGLE_base_vertex_base_instance" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest From 73ba2b9d64f0889bb9bde4607ed1161991ec162e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 11 May 2021 13:59:29 +0200 Subject: [PATCH 20/93] Shaders: benchmark all shader variants. Took me a while (several years?) to figure out a way to benchmark this without basically duplicating the testing effort and without new variants being too hard to add. --- package/ci/appveyor-desktop-gles.bat | 2 +- package/ci/appveyor-desktop-mingw.bat | 2 +- package/ci/appveyor-desktop.bat | 2 +- package/ci/emscripten.sh | 2 +- package/ci/travis-android-gles.sh | 2 +- package/ci/travis-ios-simulator.sh | 2 +- package/ci/unix-desktop.sh | 2 +- .../Shaders/Test/BenchmarkFiles/trivial.tga | Bin 0 -> 10258 bytes src/Magnum/Shaders/Test/CMakeLists.txt | 20 + .../Shaders/Test/ShadersGLBenchmark.cpp | 800 ++++++++++++++++++ 10 files changed, 827 insertions(+), 7 deletions(-) create mode 100644 src/Magnum/Shaders/Test/BenchmarkFiles/trivial.tga create mode 100644 src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp diff --git a/package/ci/appveyor-desktop-gles.bat b/package/ci/appveyor-desktop-gles.bat index 904df3606..15ab951c8 100644 --- a/package/ci/appveyor-desktop-gles.bat +++ b/package/ci/appveyor-desktop-gles.bat @@ -58,7 +58,7 @@ cmake --build . || exit /b rem Test set CORRADE_TEST_COLOR=ON -ctest -V -E GLTest || exit /b +ctest -V -E "GLTest|GLBenchmark" || exit /b rem Test install, after running the tests as for them it shouldn't be needed cmake --build . --target install || exit /b diff --git a/package/ci/appveyor-desktop-mingw.bat b/package/ci/appveyor-desktop-mingw.bat index 59d3a1edd..33811414f 100644 --- a/package/ci/appveyor-desktop-mingw.bat +++ b/package/ci/appveyor-desktop-mingw.bat @@ -60,7 +60,7 @@ cmake --build . || exit /b rem Test set CORRADE_TEST_COLOR=ON -ctest -V -E "(GL|Vk)Test" || exit /b +ctest -V -E "GLTest|GLBenchmark|VkTest" || exit /b rem Test install, after running the tests as for them it shouldn't be needed cmake --build . --target install || exit /b diff --git a/package/ci/appveyor-desktop.bat b/package/ci/appveyor-desktop.bat index 260f23b13..9972e9cdf 100644 --- a/package/ci/appveyor-desktop.bat +++ b/package/ci/appveyor-desktop.bat @@ -69,7 +69,7 @@ cmake --build . || exit /b rem Test set CORRADE_TEST_COLOR=ON -ctest -V -E "(GL|Vk)Test" || exit /b +ctest -V -E "GLTest|GLBenchmark|VkTest" || exit /b rem Test install, after running the tests as for them it shouldn't be needed cmake --build . --target install || exit /b diff --git a/package/ci/emscripten.sh b/package/ci/emscripten.sh index 25a9df853..66bc6a8f8 100755 --- a/package/ci/emscripten.sh +++ b/package/ci/emscripten.sh @@ -73,7 +73,7 @@ cmake .. \ ninja # Test -CORRADE_TEST_COLOR=ON ctest -V -E "(GL|AL)Test" +CORRADE_TEST_COLOR=ON ctest -V -E "GLTest|GLBenchmark|ALTest" # Test install, after running the tests as for them it shouldn't be needed ninja install diff --git a/package/ci/travis-android-gles.sh b/package/ci/travis-android-gles.sh index 9d9e062bf..4b4521e02 100755 --- a/package/ci/travis-android-gles.sh +++ b/package/ci/travis-android-gles.sh @@ -83,7 +83,7 @@ ninja -j4 echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a emulator -avd test -no-audio -no-window & android-wait-for-emulator -CORRADE_TEST_COLOR=ON ctest -V -E GLTest +CORRADE_TEST_COLOR=ON ctest -V -E "GLTest|GLBenchmark" # Test install, after running the tests as for them it shouldn't be needed ninja install diff --git a/package/ci/travis-ios-simulator.sh b/package/ci/travis-ios-simulator.sh index b887aecf4..896fe5225 100755 --- a/package/ci/travis-ios-simulator.sh +++ b/package/ci/travis-ios-simulator.sh @@ -70,7 +70,7 @@ set -o pipefail && cmake --build . --config Release | xcpretty # TODO: find a better way to avoid # Library not loaded: /System/Library/Frameworks/OpenGLES.framework/OpenGLES # error -DYLD_FALLBACK_LIBRARY_PATH=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/OpenGLES.framework/ DYLD_FALLBACK_FRAMEWORK_PATH=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks CORRADE_TEST_COLOR=ON ctest -V -C Release -E GLTest +DYLD_FALLBACK_LIBRARY_PATH=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/OpenGLES.framework/ DYLD_FALLBACK_FRAMEWORK_PATH=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks CORRADE_TEST_COLOR=ON ctest -V -C Release -E "GLTest|GLBenchmark" # Test install, after running the tests as for them it shouldn't be needed set -o pipefail && cmake --build . --config Release --target install | xcpretty diff --git a/package/ci/unix-desktop.sh b/package/ci/unix-desktop.sh index b3dfb30ad..84964def9 100755 --- a/package/ci/unix-desktop.sh +++ b/package/ci/unix-desktop.sh @@ -67,7 +67,7 @@ cmake .. \ -DBUILD_PLUGINS_STATIC=$BUILD_STATIC \ -G Ninja ninja $NINJA_JOBS -ASAN_OPTIONS="color=always" LSAN_OPTIONS="color=always suppressions=$(pwd)/../package/ci/leaksanitizer.conf" TSAN_OPTIONS="color=always" CORRADE_TEST_COLOR=ON ctest -V -E "(GL|Vk)Test" +ASAN_OPTIONS="color=always" LSAN_OPTIONS="color=always suppressions=$(pwd)/../package/ci/leaksanitizer.conf" TSAN_OPTIONS="color=always" CORRADE_TEST_COLOR=ON ctest -V -E "GLTest|GLBenchmark|VkTest" # Test install, after running the tests as for them it shouldn't be needed ninja install diff --git a/src/Magnum/Shaders/Test/BenchmarkFiles/trivial.tga b/src/Magnum/Shaders/Test/BenchmarkFiles/trivial.tga new file mode 100644 index 0000000000000000000000000000000000000000..5042eb5fc767b5f32f55af29a05706bad8daee8a GIT binary patch literal 10258 zcmeIuK@9*f2n4VTuVpIc-z59tfuxntYwAz<>b*1`HT5V8DO@0|pEjSU2ziI*;0b literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/CMakeLists.txt b/src/Magnum/Shaders/Test/CMakeLists.txt index 214681e51..cbdaa1e40 100644 --- a/src/Magnum/Shaders/Test/CMakeLists.txt +++ b/src/Magnum/Shaders/Test/CMakeLists.txt @@ -317,6 +317,25 @@ if(BUILD_GL_TESTS) endif() endif() + corrade_add_test(ShadersGLBenchmark ShadersGLBenchmark.cpp + LIBRARIES + MagnumDebugTools + MagnumMeshTools + MagnumPrimitives + MagnumShaders + MagnumOpenGLTester + FILES + BenchmarkFiles/trivial.tga) + target_include_directories(ShadersGLBenchmark PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) + if(BUILD_PLUGINS_STATIC) + if(WITH_ANYIMAGEIMPORTER) + target_link_libraries(ShadersGLBenchmark PRIVATE AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + target_link_libraries(ShadersGLBenchmark PRIVATE TgaImporter) + endif() + endif() + if(CORRADE_TARGET_IOS) set_source_files_properties( TestFiles @@ -336,5 +355,6 @@ if(BUILD_GL_TESTS) ShadersPhongGLTest ShadersVectorGLTest ShadersVertexColorGLTest + ShadersGLBenchmark PROPERTIES FOLDER "Magnum/Shaders/Test") endif() diff --git a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp new file mode 100644 index 000000000..b92d3f93d --- /dev/null +++ b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp @@ -0,0 +1,800 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include + +#include "Magnum/Image.h" +#include "Magnum/ImageView.h" +#include "Magnum/PixelFormat.h" +#include "Magnum/DebugTools/CompareImage.h" +#include "Magnum/GL/Extensions.h" +#include "Magnum/GL/Framebuffer.h" +#include "Magnum/GL/Mesh.h" +#include "Magnum/GL/OpenGLTester.h" +#include "Magnum/GL/Renderbuffer.h" +#include "Magnum/GL/RenderbufferFormat.h" +#include "Magnum/GL/Texture.h" +#include "Magnum/GL/TextureFormat.h" +#include "Magnum/Math/Color.h" +#include "Magnum/Math/Matrix4.h" +#include "Magnum/MeshTools/Compile.h" +#include "Magnum/MeshTools/Duplicate.h" +#include "Magnum/MeshTools/Interleave.h" +#include "Magnum/Primitives/Grid.h" +#include "Magnum/Shaders/DistanceFieldVectorGL.h" +#include "Magnum/Shaders/FlatGL.h" +#include "Magnum/Shaders/MeshVisualizerGL.h" +#include "Magnum/Shaders/PhongGL.h" +#include "Magnum/Shaders/VertexColorGL.h" +#include "Magnum/Shaders/VectorGL.h" +#include "Magnum/Trade/AbstractImporter.h" +#include "Magnum/Trade/MeshData.h" + +#include "configure.h" + +namespace Magnum { namespace Shaders { namespace Test { namespace { + +/* + The goal of this is to not duplicate all testing work here, but instead + have a set of simple-to-setup benchmarks with trivial output that allow for + measuring cost of particular shader features or seeing performance + differences between different implementations of the same (e.g., using the + VertexColor shader vs Flat with vertex colors enabled). Thus: + + - all shaders render the same mesh, 2D shaders only ignore the Z + coordinate (so it should be possible to compare the perf between 2D and + 3D) + - the mesh contains all attributes the shader might ever need including + instanced ones, to avoid differences caused by different memory access + patterns + - transformation and projection is identity + - textures are always single-pixel to measure the sampler overhead, not + memory access overhead + - if texture transformation is enabled, it's identity + - if instancing features are enabled, there's exactly one instance + - if alpha mask is enabled, it's 0.0 + - uniforms / binding overhead is not included in the benchmark +*/ + +struct ShadersGLBenchmark: GL::OpenGLTester { + explicit ShadersGLBenchmark(); + + void renderSetup(); + void renderTeardown(); + + template void flat(); + void phong(); + template void vertexColor(); + template void vector(); + template void distanceFieldVector(); + void meshVisualizer2D(); + void meshVisualizer3D(); + /** @todo mesh visualizer TBN, how to verify output? */ + + private: + PluginManager::Manager _manager{"nonexistent"}; + + GL::Renderbuffer _color; + #ifndef MAGNUM_TARGET_GLES2 + GL::Renderbuffer _objectId{NoCreate}; + #endif + GL::Framebuffer _framebuffer; + + GL::Buffer _indices, _vertices; + GL::Mesh _mesh, _meshInstanced, _meshDuplicated; + + GL::Texture2D _textureWhite, _textureBlue; +}; + +using namespace Math::Literals; + +constexpr Vector2i GridSubdivisions{64, 64}; +constexpr Vector2i RenderSize{512, 512}; +constexpr std::size_t WarmupIterations{100}; +constexpr std::size_t BenchmarkIterations{1000}; +constexpr std::size_t BenchmarkRepeats{4}; + +const struct { + const char* name; + FlatGL2D::Flags flags; +} FlatData[] { + {"", {}}, + {"vertex color", FlatGL2D::Flag::VertexColor}, + #ifndef MAGNUM_TARGET_GLES2 + {"object ID", FlatGL2D::Flag::ObjectId}, + #endif + {"textured", FlatGL2D::Flag::Textured}, + {"textured + alpha mask", FlatGL2D::Flag::Textured|FlatGL2D::Flag::AlphaMask}, + {"texture transformation", FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation}, + {"instanced transformation", FlatGL2D::Flag::InstancedTransformation}, + {"instanced transformation + color", FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::VertexColor}, + #ifndef MAGNUM_TARGET_GLES2 + {"instanced transformation + object ID", FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::InstancedObjectId}, + #endif + {"instanced transformation + texture offset", FlatGL2D::Flag::Textured|FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::InstancedTextureOffset}, +}; + +const struct { + const char* name; + PhongGL::Flags flags; + UnsignedInt lights; +} PhongData[] { + {"", {}, 1}, + {"zero lights", {}, 0}, + {"five lights", {}, 5}, + {"vertex color", PhongGL::Flag::VertexColor, 1}, + #ifndef MAGNUM_TARGET_GLES2 + {"object ID", PhongGL::Flag::ObjectId, 1}, + #endif + {"diffuse texture", PhongGL::Flag::DiffuseTexture, 1}, + {"ADS textures", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture, 1}, + {"ADS textures + alpha mask", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::AlphaMask, 1}, + {"ADS textures + transformation", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureTransformation, 1}, + {"normal texture", PhongGL::Flag::NormalTexture, 1}, + {"normal texture with separate bitangent", PhongGL::Flag::NormalTexture|PhongGL::Flag::Bitangent, 1}, + {"instanced transformation", PhongGL::Flag::InstancedTransformation, 1}, + {"instanced transformation + color", PhongGL::Flag::InstancedTransformation|PhongGL::Flag::VertexColor, 1}, + #ifndef MAGNUM_TARGET_GLES2 + {"instanced transformation + object ID", PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedObjectId, 1}, + #endif + {"instanced transformation + ADS texture offset", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedTextureOffset, 1}, +}; + +const struct { + const char* name; + VectorGL2D::Flags flags; +} VectorData[] { + {"", {}}, + {"texture transformation", VectorGL2D::Flag::TextureTransformation} +}; + +const struct { + const char* name; + DistanceFieldVectorGL2D::Flags flags; +} DistanceFieldVectorData[] { + {"", {}}, + {"texture transformation", DistanceFieldVectorGL2D::Flag::TextureTransformation} +}; + +const struct { + const char* name; + MeshVisualizerGL2D::Flags flags; +} MeshVisualizer2DData[] { + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + {"wireframe", MeshVisualizerGL2D::Flag::Wireframe}, + #endif + {"wireframe w/o a GS", MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader}, + #ifndef MAGNUM_TARGET_GLES2 + {"instanced object ID", MeshVisualizerGL2D::Flag::InstancedObjectId}, + {"vertex ID", MeshVisualizerGL2D::Flag::VertexId}, + #ifndef MAGNUM_TARGET_WEBGL + {"primitive ID", MeshVisualizerGL2D::Flag::PrimitiveId}, + {"primitive ID from vertex ID", MeshVisualizerGL2D::Flag::PrimitiveIdFromVertexId}, + #endif + #endif +}; + +const struct { + const char* name; + MeshVisualizerGL3D::Flags flags; +} MeshVisualizer3DData[] { + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + {"wireframe", MeshVisualizerGL3D::Flag::Wireframe}, + #endif + {"wireframe w/o a GS", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader}, + #ifndef MAGNUM_TARGET_GLES2 + {"instanced object ID", MeshVisualizerGL3D::Flag::InstancedObjectId}, + {"vertex ID", MeshVisualizerGL3D::Flag::VertexId}, + #ifndef MAGNUM_TARGET_WEBGL + {"primitive ID", MeshVisualizerGL3D::Flag::PrimitiveId}, + {"primitive ID from vertex ID", MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId}, + #endif + #endif +}; + +ShadersGLBenchmark::ShadersGLBenchmark(): _framebuffer{{{}, RenderSize}} { + addInstancedBenchmarks({&ShadersGLBenchmark::flat<2>, + &ShadersGLBenchmark::flat<3>}, + BenchmarkRepeats, Containers::arraySize(FlatData), + &ShadersGLBenchmark::renderSetup, + &ShadersGLBenchmark::renderTeardown, + BenchmarkType::GpuTime); + + addInstancedBenchmarks({&ShadersGLBenchmark::phong}, + BenchmarkRepeats, Containers::arraySize(PhongData), + &ShadersGLBenchmark::renderSetup, + &ShadersGLBenchmark::renderTeardown, + BenchmarkType::GpuTime); + + addBenchmarks({&ShadersGLBenchmark::vertexColor<2>, + &ShadersGLBenchmark::vertexColor<3>}, + BenchmarkRepeats, + &ShadersGLBenchmark::renderSetup, + &ShadersGLBenchmark::renderTeardown, + BenchmarkType::GpuTime); + + addInstancedBenchmarks({&ShadersGLBenchmark::vector<2>, + &ShadersGLBenchmark::vector<3>}, + BenchmarkRepeats, Containers::arraySize(VectorData), + &ShadersGLBenchmark::renderSetup, + &ShadersGLBenchmark::renderTeardown, + BenchmarkType::GpuTime); + + addInstancedBenchmarks({&ShadersGLBenchmark::distanceFieldVector<2>, + &ShadersGLBenchmark::distanceFieldVector<3>}, + BenchmarkRepeats, Containers::arraySize(DistanceFieldVectorData), + &ShadersGLBenchmark::renderSetup, + &ShadersGLBenchmark::renderTeardown, + BenchmarkType::GpuTime); + + addInstancedBenchmarks({&ShadersGLBenchmark::meshVisualizer2D}, + BenchmarkRepeats, Containers::arraySize(MeshVisualizer2DData), + &ShadersGLBenchmark::renderSetup, + &ShadersGLBenchmark::renderTeardown, + BenchmarkType::GpuTime); + + addInstancedBenchmarks({&ShadersGLBenchmark::meshVisualizer3D}, + BenchmarkRepeats, Containers::arraySize(MeshVisualizer3DData), + &ShadersGLBenchmark::renderSetup, + &ShadersGLBenchmark::renderTeardown, + BenchmarkType::GpuTime); + + /* Set up the framebuffer */ + _color.setStorage( + #if !defined(MAGNUM_TARGET_GLES2) || !defined(MAGNUM_TARGET_WEBGL) + GL::RenderbufferFormat::RGBA8, + #else + GL::RenderbufferFormat::RGBA4, + #endif + RenderSize); + _framebuffer.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, _color) + .bind(); + #ifndef MAGNUM_TARGET_GLES2 + /* If we don't have EXT_gpu_shader4, we likely don't have integer + framebuffers either (Mesa's Zink), so skip setting up integer + attachments to avoid GL errors */ + #ifndef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + #endif + { + _objectId = GL::Renderbuffer{}; + _objectId.setStorage(GL::RenderbufferFormat::R32UI, RenderSize); + _framebuffer.attachRenderbuffer(GL::Framebuffer::ColorAttachment{1}, _objectId) + .mapForDraw({ + {FlatGL2D::ColorOutput, GL::Framebuffer::ColorAttachment{0}}, + {FlatGL2D::ObjectIdOutput, GL::Framebuffer::ColorAttachment{1}} + }); + } + #endif + + /* Set up the mesh */ + { + Trade::MeshData data = Primitives::grid3DSolid(GridSubdivisions, + Primitives::GridFlag::TextureCoordinates| + Primitives::GridFlag::Normals| + Primitives::GridFlag::Tangents); + Containers::Array vertexColors{DirectInit, data.vertexCount(), 0xffffffff_rgbaf}; + Containers::Array bitangents{DirectInit, data.vertexCount(), Vector3{0.0f, 1.0f, 0.0f}}; + Trade::MeshData dataWithVertexColors = MeshTools::interleave(std::move(data), { + Trade::MeshAttributeData{Trade::MeshAttribute::Color, arrayView(vertexColors)}, + Trade::MeshAttributeData{Trade::MeshAttribute::Bitangent, arrayView(bitangents)} + }); + _indices.setData(dataWithVertexColors.indexData()); + _vertices.setData(dataWithVertexColors.vertexData()); + _mesh = MeshTools::compile(dataWithVertexColors, _indices, _vertices); + + /* Instanced variant, if the divisor-related extension is supported */ + #ifndef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + #elif defined(MAGNUM_TARGET_GLES2) + #ifndef MAGNUM_TARGET_WEBGL + if(GL::Context::current().isExtensionSupported() || + GL::Context::current().isExtensionSupported() || + GL::Context::current().isExtensionSupported()) + #else + if(GL::Context::current().isExtensionSupported()) + #endif + #endif + { + _meshInstanced = MeshTools::compile(dataWithVertexColors, _indices, _vertices); + struct { + /* Given the way the matrix attribute is specified (column by + column), it should work for 2D as well */ + Matrix4 transformation{Math::IdentityInit}; + Matrix3x3 normalMatrix{Math::IdentityInit}; + Vector2 textureOffset{0.0f, 0.0f}; + Color4 color{0xffffffff_rgbaf}; + UnsignedInt objectId{0}; + } instanceData[1]; + _meshInstanced.addVertexBufferInstanced(GL::Buffer{instanceData}, 1, 0, + GenericGL3D::TransformationMatrix{}, + GenericGL3D::NormalMatrix{}, + GenericGL3D::TextureOffset{}, + #ifndef MAGNUM_TARGET_GLES2 + GenericGL3D::ObjectId{} + #else + sizeof(UnsignedInt) + #endif + ); + /** @todo hmm, this doesn't really issue an instanced draw call, does + that matter? */ + _meshInstanced.setInstanceCount(1); + } + + /* Non-indexed variant for GS-less wireframe drawing */ + _meshDuplicated = MeshTools::compile(MeshTools::duplicate(dataWithVertexColors)); + } + + /* Set up the textures */ + { + const Color4ub white[1] { 0xffffffff_rgba }; + _textureWhite.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + GL::TextureFormat::RGBA8 + #else + GL::TextureFormat::RGBA + #endif + , {1, 1}) + .setSubImage(0, {}, ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, white}); + } { + const Color4ub blue[1] { 0x0000ffff_rgba }; + _textureBlue.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + GL::TextureFormat::RGBA8 + #else + GL::TextureFormat::RGBA + #endif + , {1, 1}) + .setSubImage(0, {}, ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, blue}); + } + + /* 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 + CORRADE_INTERNAL_ASSERT_OUTPUT(_manager.load(ANYIMAGEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + #ifdef TGAIMPORTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT_OUTPUT(_manager.load(TGAIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif +} + +void ShadersGLBenchmark::renderSetup() { + #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + #endif + { + _framebuffer + .clearColor(0, Color4{}) + .clearColor(1, Vector4ui{}); + } + #ifndef MAGNUM_TARGET_GLES + else + #endif + #endif + { + _framebuffer.clear(GL::FramebufferClear::Color); + } +} + +void ShadersGLBenchmark::renderTeardown() { + /* Nothing to do here */ +} + +template void ShadersGLBenchmark::flat() { + auto&& data = FlatData[testCaseInstanceId()]; + setTestCaseTemplateName(Utility::formatString("{}", dimensions)); + setTestCaseDescription(data.name); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + FlatGL shader{data.flags}; + if(data.flags >= FlatGL2D::Flag::AlphaMask) + shader.setAlphaMask(0.0f); + if(data.flags >= FlatGL2D::Flag::Textured) + shader.bindTexture(_textureWhite); + + GL::Mesh* mesh; + /* InstancedTextureOffset is a superset of TextureTransformation, so + remove those bits first when deciding if instanced */ + if((data.flags & ~FlatGL2D::Flag::TextureTransformation) & (FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::InstancedTextureOffset + #ifndef MAGNUM_TARGET_GLES2 + |FlatGL2D::Flag::InstancedObjectId + #endif + )) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::instanced_arrays::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("Required extension is not available."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::instanced_arrays::string() << "is not supported."); + #endif + #endif + mesh = &_meshInstanced; + } else { + mesh = &_mesh; + } + + /* Warmup run */ + /** @todo make this possible to do inside CORRADE_BENCHMARK() */ + for(std::size_t i = 0; i != 100; ++i) + shader.draw(*mesh); + + CORRADE_BENCHMARK(BenchmarkIterations) + shader.draw(*mesh); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE_WITH(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}), + Utility::Directory::join(SHADERS_TEST_DIR, "BenchmarkFiles/trivial.tga"), + DebugTools::CompareImageToFile{_manager}); +} + +void ShadersGLBenchmark::phong() { + auto&& data = PhongData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + PhongGL shader{data.flags, data.lights}; + /* White ambient so we always have a white output */ + shader.setAmbientColor(0xffffffff_rgbaf); + if(data.flags >= PhongGL::Flag::AlphaMask) + shader.setAlphaMask(0.0f); + if(data.flags >= PhongGL::Flag::AmbientTexture) + shader.bindAmbientTexture(_textureWhite); + if(data.flags >= PhongGL::Flag::DiffuseTexture) + shader.bindDiffuseTexture(_textureWhite); + if(data.flags >= PhongGL::Flag::SpecularTexture) + shader.bindSpecularTexture(_textureWhite); + if(data.flags >= PhongGL::Flag::NormalTexture) + shader.bindNormalTexture(_textureBlue); + + GL::Mesh* mesh; + /* InstancedTextureOffset is a superset of TextureTransformation, so + remove those bits first when deciding if instanced */ + if((data.flags & ~PhongGL::Flag::TextureTransformation) & (PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedTextureOffset + #ifndef MAGNUM_TARGET_GLES2 + |PhongGL::Flag::InstancedObjectId + #endif + )) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::draw_instanced::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("Required extension is not available."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::instanced_arrays::string() << "is not supported."); + #endif + #endif + mesh = &_meshInstanced; + } else { + mesh = &_mesh; + } + + /* Warmup run */ + /** @todo make this possible to do inside CORRADE_BENCHMARK() */ + for(std::size_t i = 0; i != WarmupIterations; ++i) + shader.draw(*mesh); + + CORRADE_BENCHMARK(BenchmarkIterations) + shader.draw(*mesh); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE_WITH(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}), + Utility::Directory::join(SHADERS_TEST_DIR, "BenchmarkFiles/trivial.tga"), + DebugTools::CompareImageToFile{_manager}); +} + +template void ShadersGLBenchmark::vertexColor() { + setTestCaseTemplateName(Utility::formatString("{}", dimensions)); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + VertexColorGL shader; + + /* Warmup run */ + /** @todo make this possible to do inside CORRADE_BENCHMARK() */ + for(std::size_t i = 0; i != WarmupIterations; ++i) + shader.draw(_mesh); + + CORRADE_BENCHMARK(BenchmarkIterations) + shader.draw(_mesh); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE_WITH(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}), + Utility::Directory::join(SHADERS_TEST_DIR, "BenchmarkFiles/trivial.tga"), + DebugTools::CompareImageToFile{_manager}); +} + +template void ShadersGLBenchmark::vector() { + auto&& data = VectorData[testCaseInstanceId()]; + setTestCaseTemplateName(Utility::formatString("{}", dimensions)); + setTestCaseDescription(data.name); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + VectorGL shader{data.flags}; + shader.bindVectorTexture(_textureWhite); + + /* Warmup run */ + /** @todo make this possible to do inside CORRADE_BENCHMARK() */ + for(std::size_t i = 0; i != WarmupIterations; ++i) + shader.draw(_mesh); + + CORRADE_BENCHMARK(BenchmarkIterations) + shader.draw(_mesh); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE_WITH(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}), + Utility::Directory::join(SHADERS_TEST_DIR, "BenchmarkFiles/trivial.tga"), + DebugTools::CompareImageToFile{_manager}); +} + +template void ShadersGLBenchmark::distanceFieldVector() { + auto&& data = DistanceFieldVectorData[testCaseInstanceId()]; + setTestCaseTemplateName(Utility::formatString("{}", dimensions)); + setTestCaseDescription(data.name); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + DistanceFieldVectorGL shader{data.flags}; + shader.bindVectorTexture(_textureWhite); + + /* Warmup run */ + /** @todo make this possible to do inside CORRADE_BENCHMARK() */ + for(std::size_t i = 0; i != WarmupIterations; ++i) + shader.draw(_mesh); + + CORRADE_BENCHMARK(BenchmarkIterations) + shader.draw(_mesh); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE_WITH(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}), + Utility::Directory::join(SHADERS_TEST_DIR, "BenchmarkFiles/trivial.tga"), + DebugTools::CompareImageToFile{_manager}); +} + +void ShadersGLBenchmark::meshVisualizer2D() { + auto&& data = MeshVisualizer2DData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + /* Checks verbatim copied from MeshVisualizerGLTest::construct2D() */ + #ifndef MAGNUM_TARGET_GLES + if((data.flags & MeshVisualizerGL2D::Flag::InstancedObjectId) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); + #endif + + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(data.flags >= MeshVisualizerGL2D::Flag::PrimitiveIdFromVertexId && + #ifndef MAGNUM_TARGET_GLES + !GL::Context::current().isVersionSupported(GL::Version::GL300) + #else + !GL::Context::current().isVersionSupported(GL::Version::GLES300) + #endif + ) CORRADE_SKIP("gl_VertexID not supported."); + #endif + + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(data.flags & MeshVisualizerGL2D::Flag::PrimitiveId && !(data.flags >= MeshVisualizerGL2D::Flag::PrimitiveIdFromVertexId) && + #ifndef MAGNUM_TARGET_GLES + !GL::Context::current().isVersionSupported(GL::Version::GL320) + #else + !GL::Context::current().isVersionSupported(GL::Version::GLES320) + #endif + ) CORRADE_SKIP("gl_PrimitiveID not supported."); + #endif + + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if((data.flags & MeshVisualizerGL2D::Flag::Wireframe) && !(data.flags & MeshVisualizerGL2D::Flag::NoGeometryShader)) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() << "is not supported."); + #endif + + #ifdef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + CORRADE_INFO("Using" << GL::Extensions::NV::shader_noperspective_interpolation::string()); + #endif + } + #endif + + MeshVisualizerGL2D shader{data.flags}; + shader.setViewportSize(Vector2{RenderSize}); + if(data.flags >= MeshVisualizerGL2D::Flag::Wireframe) + shader.setWireframeColor(0xffffffff_rgbaf); + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & (MeshVisualizerGL2D::Flag::InstancedObjectId|MeshVisualizerGL2D::Flag::VertexId|MeshVisualizerGL2D::Flag::PrimitiveIdFromVertexId + #ifndef MAGNUM_TARGET_WEBGL + |MeshVisualizerGL2D::Flag::PrimitiveId + #endif + )) + shader.bindColorMapTexture(_textureWhite); + #endif + + GL::Mesh* mesh; + if(data.flags >= MeshVisualizerGL2D::Flag::NoGeometryShader) { + mesh = &_meshDuplicated; + } + #ifndef MAGNUM_TARGET_GLES2 + else if(data.flags & MeshVisualizerGL2D::Flag::InstancedObjectId) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::draw_instanced::string() << "is not supported."); + #endif + mesh = &_meshInstanced; + } + #endif + else { + mesh = &_mesh; + } + + /* Warmup run */ + /** @todo make this possible to do inside CORRADE_BENCHMARK() */ + for(std::size_t i = 0; i != WarmupIterations; ++i) + shader.draw(*mesh); + + CORRADE_BENCHMARK(BenchmarkIterations) + shader.draw(*mesh); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE_WITH(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}), + Utility::Directory::join(SHADERS_TEST_DIR, "BenchmarkFiles/trivial.tga"), + DebugTools::CompareImageToFile{_manager}); +} + +void ShadersGLBenchmark::meshVisualizer3D() { + auto&& data = MeshVisualizer3DData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + /* Checks verbatim copied from MeshVisualizerGLTest:.construct3D() */ + #ifndef MAGNUM_TARGET_GLES + if((data.flags & MeshVisualizerGL3D::Flag::InstancedObjectId) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); + #endif + + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(data.flags >= MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId && + #ifndef MAGNUM_TARGET_GLES + !GL::Context::current().isVersionSupported(GL::Version::GL300) + #else + !GL::Context::current().isVersionSupported(GL::Version::GLES300) + #endif + ) CORRADE_SKIP("gl_VertexID not supported."); + #endif + + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(data.flags & MeshVisualizerGL3D::Flag::PrimitiveId && !(data.flags >= MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId) && + #ifndef MAGNUM_TARGET_GLES + !GL::Context::current().isVersionSupported(GL::Version::GL320) + #else + !GL::Context::current().isVersionSupported(GL::Version::GLES320) + #endif + ) CORRADE_SKIP("gl_PrimitiveID not supported."); + #endif + + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(((data.flags & MeshVisualizerGL3D::Flag::Wireframe) && !(data.flags & MeshVisualizerGL3D::Flag::NoGeometryShader)) || (data.flags & (MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentDirection|MeshVisualizerGL3D::Flag::BitangentFromTangentDirection|MeshVisualizerGL3D::Flag::NormalDirection))) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() << "is not supported."); + #endif + + #ifdef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + CORRADE_INFO("Using" << GL::Extensions::NV::shader_noperspective_interpolation::string()); + #endif + } + #endif + + MeshVisualizerGL3D shader{data.flags}; + shader.setViewportSize(Vector2{RenderSize}); + if(data.flags >= MeshVisualizerGL3D::Flag::Wireframe) + shader.setWireframeColor(0xffffffff_rgbaf); + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & (MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::VertexId|MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId + #ifndef MAGNUM_TARGET_WEBGL + |MeshVisualizerGL3D::Flag::PrimitiveId + #endif + )) + shader.bindColorMapTexture(_textureWhite); + #endif + + GL::Mesh* mesh; + if(data.flags >= MeshVisualizerGL3D::Flag::NoGeometryShader) + mesh = &_meshDuplicated; + #ifndef MAGNUM_TARGET_GLES2 + else if(data.flags & MeshVisualizerGL3D::Flag::InstancedObjectId) + mesh = &_meshInstanced; + #endif + else + mesh = &_mesh; + + /* Warmup run */ + /** @todo make this possible to do inside CORRADE_BENCHMARK() */ + for(std::size_t i = 0; i != WarmupIterations; ++i) + shader.draw(*mesh); + + CORRADE_BENCHMARK(BenchmarkIterations) + shader.draw(*mesh); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE_WITH(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}), + Utility::Directory::join(SHADERS_TEST_DIR, "BenchmarkFiles/trivial.tga"), + DebugTools::CompareImageToFile{_manager}); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Shaders::Test::ShadersGLBenchmark) From 37c8cfd38440d5096aaec87fa15855e9e9eb0c54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 20 May 2021 13:14:15 +0200 Subject: [PATCH 21/93] Shaders: MeshVisualizer color map binding was done in a wrong branch. It probably didn't matter as much as the only platform without ARB_explicit_uniform_location is Mac, which doesn't have ARB_shading_language_420pack either. --- src/Magnum/Shaders/MeshVisualizerGL.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Magnum/Shaders/MeshVisualizerGL.cpp b/src/Magnum/Shaders/MeshVisualizerGL.cpp index 9fd3c2398..a81eeb037 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.cpp +++ b/src/Magnum/Shaders/MeshVisualizerGL.cpp @@ -293,11 +293,21 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags): Implementation::MeshV #ifndef MAGNUM_TARGET_GLES2 if(flags & (Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId)) { _colorMapOffsetScaleUniform = uniformLocation("colorMapOffsetScale"); - setUniform(uniformLocation("colorMapTexture"), ColorMapTextureUnit); } #endif } + #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_GLES + if(flags && !context.isExtensionSupported(version)) + #endif + { + if(flags & (Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId)) { + setUniform(uniformLocation("colorMapTexture"), ColorMapTextureUnit); + } + } + #endif + /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ #ifdef MAGNUM_TARGET_GLES setTransformationProjectionMatrix(Matrix3{Math::IdentityInit}); @@ -509,7 +519,6 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags): Implementation::MeshV #ifndef MAGNUM_TARGET_GLES2 if(flags & (Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId)) { _colorMapOffsetScaleUniform = uniformLocation("colorMapOffsetScale"); - setUniform(uniformLocation("colorMapTexture"), ColorMapTextureUnit); } #endif #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) @@ -521,6 +530,17 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags): Implementation::MeshV #endif } + #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_GLES + if(flags && !context.isExtensionSupported(version)) + #endif + { + if(flags & (Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId)) { + setUniform(uniformLocation("colorMapTexture"), ColorMapTextureUnit); + } + } + #endif + /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ #ifdef MAGNUM_TARGET_GLES setTransformationMatrix(Matrix4{Math::IdentityInit}); From ef9da0ec96274a43eea966e1232101f440b2ae62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 20 May 2021 16:47:30 +0200 Subject: [PATCH 22/93] Shaders: add UBO support to all shaders. --- doc/changelog.dox | 2 + package/archlinux/PKGBUILD | 1 + package/archlinux/PKGBUILD-coverage | 1 + package/archlinux/PKGBUILD-release | 1 + src/Magnum/Shaders/CMakeLists.txt | 17 +- src/Magnum/Shaders/DistanceFieldVector.frag | 56 + src/Magnum/Shaders/DistanceFieldVector.h | 223 +- src/Magnum/Shaders/DistanceFieldVectorGL.cpp | 223 +- src/Magnum/Shaders/DistanceFieldVectorGL.h | 244 +- src/Magnum/Shaders/Flat.frag | 47 + src/Magnum/Shaders/Flat.h | 132 +- src/Magnum/Shaders/Flat.vert | 65 + src/Magnum/Shaders/FlatGL.cpp | 197 +- src/Magnum/Shaders/FlatGL.h | 208 +- src/Magnum/Shaders/Generic.h | 430 +++- src/Magnum/Shaders/MeshVisualizer.frag | 79 + src/Magnum/Shaders/MeshVisualizer.geom | 107 +- src/Magnum/Shaders/MeshVisualizer.h | 416 +++- src/Magnum/Shaders/MeshVisualizer.vert | 108 + src/Magnum/Shaders/MeshVisualizerGL.cpp | 532 ++++- src/Magnum/Shaders/MeshVisualizerGL.h | 479 +++- src/Magnum/Shaders/Phong.frag | 137 +- src/Magnum/Shaders/Phong.h | 449 +++- src/Magnum/Shaders/Phong.vert | 119 +- src/Magnum/Shaders/PhongGL.cpp | 377 +++- src/Magnum/Shaders/PhongGL.h | 357 ++- src/Magnum/Shaders/Test/CMakeLists.txt | 45 +- .../Test/DistanceFieldVectorGLTest.cpp | 959 +++++++- .../Shaders/Test/DistanceFieldVectorTest.cpp | 195 ++ src/Magnum/Shaders/Test/FlatGLTest.cpp | 1675 ++++++++++++-- src/Magnum/Shaders/Test/FlatGL_Test.cpp | 4 +- src/Magnum/Shaders/Test/FlatTest.cpp | 124 ++ .../FlatTestFiles/multidraw-textured2D.tga | Bin 0 -> 2501 bytes .../FlatTestFiles/multidraw-textured3D.tga | Bin 0 -> 2477 bytes .../Test/FlatTestFiles/multidraw2D.tga | Bin 0 -> 1026 bytes .../Test/FlatTestFiles/multidraw3D.tga | Bin 0 -> 1010 bytes src/Magnum/Shaders/Test/GenericTest.cpp | 408 ++++ .../Shaders/Test/MeshVisualizerGLTest.cpp | 1959 +++++++++++++++-- .../Shaders/Test/MeshVisualizerTest.cpp | 302 +++ .../multidraw-vertexid2D.tga | Bin 0 -> 6639 bytes .../multidraw-vertexid3D.tga | Bin 0 -> 5818 bytes .../multidraw-wireframe-nogeo2D.tga | Bin 0 -> 4485 bytes .../multidraw-wireframe-nogeo3D.tga | Bin 0 -> 4183 bytes .../multidraw-wireframe-tbn3D.tga | Bin 0 -> 5096 bytes .../multidraw-wireframe2D.tga | Bin 0 -> 4494 bytes .../multidraw-wireframe3D.tga | Bin 0 -> 4221 bytes src/Magnum/Shaders/Test/PhongGLTest.cpp | 1807 +++++++++++++-- src/Magnum/Shaders/Test/PhongTest.cpp | 322 +++ .../PhongTestFiles/multidraw-textured.tga | Bin 0 -> 5524 bytes .../Shaders/Test/PhongTestFiles/multidraw.tga | Bin 0 -> 5274 bytes src/Magnum/Shaders/Test/VectorGLTest.cpp | 863 +++++++- src/Magnum/Shaders/Test/VectorTest.cpp | 118 + .../multidraw2D-distancefield.tga | Bin 0 -> 3404 bytes .../Test/VectorTestFiles/multidraw2D.tga | Bin 0 -> 3239 bytes .../multidraw3D-distancefield.tga | Bin 0 -> 3030 bytes .../Test/VectorTestFiles/multidraw3D.tga | Bin 0 -> 2597 bytes src/Magnum/Shaders/Test/VertexColorGLTest.cpp | 650 +++++- .../Shaders/Test/VertexColorGL_Test.cpp | 34 +- .../Test/VertexColorTestFiles/multidraw2D.tga | Bin 0 -> 6852 bytes .../Test/VertexColorTestFiles/multidraw3D.tga | Bin 0 -> 4828 bytes src/Magnum/Shaders/Vector.frag | 37 + src/Magnum/Shaders/Vector.h | 100 +- src/Magnum/Shaders/Vector.vert | 65 + src/Magnum/Shaders/VectorGL.cpp | 186 +- src/Magnum/Shaders/VectorGL.h | 194 +- src/Magnum/Shaders/VertexColor.vert | 46 + src/Magnum/Shaders/VertexColorGL.cpp | 140 +- src/Magnum/Shaders/VertexColorGL.h | 184 +- 68 files changed, 14367 insertions(+), 1057 deletions(-) create mode 100644 src/Magnum/Shaders/Test/DistanceFieldVectorTest.cpp create mode 100644 src/Magnum/Shaders/Test/FlatTest.cpp create mode 100644 src/Magnum/Shaders/Test/FlatTestFiles/multidraw-textured2D.tga create mode 100644 src/Magnum/Shaders/Test/FlatTestFiles/multidraw-textured3D.tga create mode 100644 src/Magnum/Shaders/Test/FlatTestFiles/multidraw2D.tga create mode 100644 src/Magnum/Shaders/Test/FlatTestFiles/multidraw3D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTest.cpp create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-vertexid2D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-vertexid3D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-wireframe-nogeo2D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-wireframe-nogeo3D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-wireframe-tbn3D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-wireframe2D.tga create mode 100644 src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-wireframe3D.tga create mode 100644 src/Magnum/Shaders/Test/PhongTest.cpp create mode 100644 src/Magnum/Shaders/Test/PhongTestFiles/multidraw-textured.tga create mode 100644 src/Magnum/Shaders/Test/PhongTestFiles/multidraw.tga create mode 100644 src/Magnum/Shaders/Test/VectorTest.cpp create mode 100644 src/Magnum/Shaders/Test/VectorTestFiles/multidraw2D-distancefield.tga create mode 100644 src/Magnum/Shaders/Test/VectorTestFiles/multidraw2D.tga create mode 100644 src/Magnum/Shaders/Test/VectorTestFiles/multidraw3D-distancefield.tga create mode 100644 src/Magnum/Shaders/Test/VectorTestFiles/multidraw3D.tga create mode 100644 src/Magnum/Shaders/Test/VertexColorTestFiles/multidraw2D.tga create mode 100644 src/Magnum/Shaders/Test/VertexColorTestFiles/multidraw3D.tga diff --git a/doc/changelog.dox b/doc/changelog.dox index 11b5cf82a..549229cff 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -147,6 +147,8 @@ See also: @subsubsection changelog-latest-new-shaders Shaders library +- All builtin shaders now have opt-in support for uniform buffers on desktop, + OpenGL ES 3.0+ and WebGL 2.0 - Added @ref Shaders::PhongGL::setNormalTextureScale(), consuming the recently added @ref Trade::MaterialAttribute::NormalTextureScale material attribute diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index 7dba2f6c7..a277dd9dd 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -72,6 +72,7 @@ check() { MAGNUM_DISABLE_EXTENSIONS="GL_ARB_direct_state_access" ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_ARB_get_texture_sub_image" ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_uniform_buffer_object" ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" ctest --output-on-failure -j5 -R GLTest # Run all Vulkan tests with SwiftShader as well diff --git a/package/archlinux/PKGBUILD-coverage b/package/archlinux/PKGBUILD-coverage index 89d33001c..1dafdf4d6 100644 --- a/package/archlinux/PKGBUILD-coverage +++ b/package/archlinux/PKGBUILD-coverage @@ -73,6 +73,7 @@ check() { MAGNUM_DISABLE_EXTENSIONS="GL_ARB_direct_state_access GL_ARB_robustness GL_ARB_multi_bind" ctest --output-on-failure -j5 -R GLTest || true MAGNUM_DISABLE_EXTENSIONS="GL_ARB_get_texture_sub_image" ctest --output-on-failure -j5 -R GLTest || true MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" ctest --output-on-failure -j5 -R GLTest || true + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_uniform_buffer_object" ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" ctest --output-on-failure -j5 -R GLTest || true # Run all Vulkan tests with SwiftShader as well diff --git a/package/archlinux/PKGBUILD-release b/package/archlinux/PKGBUILD-release index 7c6364886..bda38eda3 100644 --- a/package/archlinux/PKGBUILD-release +++ b/package/archlinux/PKGBUILD-release @@ -110,6 +110,7 @@ check() { MAGNUM_DISABLE_EXTENSIONS="GL_ARB_direct_state_access" ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_ARB_get_texture_sub_image" ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" ctest --output-on-failure -j5 -R GLTest + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_uniform_buffer_object" ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" ctest --output-on-failure -j5 -R GLTest # Run all Vulkan tests with SwiftShader as well diff --git a/src/Magnum/Shaders/CMakeLists.txt b/src/Magnum/Shaders/CMakeLists.txt index e48a91475..08551065a 100644 --- a/src/Magnum/Shaders/CMakeLists.txt +++ b/src/Magnum/Shaders/CMakeLists.txt @@ -31,8 +31,6 @@ corrade_add_resource(MagnumShaders_RESOURCES_GL resources-gl.conf) set_target_properties(MagnumShaders_RESOURCES_GL-dependencies PROPERTIES FOLDER "Magnum/Shaders") set(MagnumShaders_SRCS - VertexColorGL.cpp - ${MagnumShaders_RESOURCES_GL}) set(MagnumShaders_GracefulAssert_SRCS @@ -40,15 +38,22 @@ set(MagnumShaders_GracefulAssert_SRCS FlatGL.cpp MeshVisualizerGL.cpp PhongGL.cpp - VectorGL.cpp) + VectorGL.cpp + VertexColorGL.cpp) set(MagnumShaders_HEADERS + DistanceFieldVector.h DistanceFieldVectorGL.h + Flat.h FlatGL.h + Generic.h GenericGL.h + MeshVisualizer.h MeshVisualizerGL.h + Phong.h PhongGL.h Shaders.h + Vector.h VectorGL.h VertexColorGL.h @@ -56,12 +61,6 @@ set(MagnumShaders_HEADERS if(MAGNUM_BUILD_DEPRECATED) list(APPEND MagnumShaders_HEADERS - DistanceFieldVector.h - Flat.h - Generic.h - MeshVisualizer.h - Phong.h - Vector.h VertexColor.h) endif() diff --git a/src/Magnum/Shaders/DistanceFieldVector.frag b/src/Magnum/Shaders/DistanceFieldVector.frag index 2f45fb27e..4f292e38f 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.frag +++ b/src/Magnum/Shaders/DistanceFieldVector.frag @@ -29,8 +29,13 @@ #define texture texture2D #endif +#ifndef RUNTIME_CONST +#define const +#endif + /* Uniforms */ +#ifndef UNIFORM_BUFFERS #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 2) #endif @@ -63,6 +68,49 @@ uniform lowp float smoothness #endif ; +/* Uniform buffers */ + +#else +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 0) +#endif +uniform highp uint drawOffset + #ifndef GL_ES + = 0u + #endif + ; + +struct DrawUniform { + highp uvec4 materialIdReservedReservedReservedReserved; + #define draw_materialIdReserved materialIdReservedReservedReservedReserved.x +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 2 + #endif +) uniform Draw { + DrawUniform draws[DRAW_COUNT]; +}; + +struct MaterialUniform { + lowp vec4 color; + lowp vec4 reserved; + lowp vec4 outlineColor; + lowp vec4 outlineRangeSmoothnessReserved; + #define material_outlineRange outlineRangeSmoothnessReserved.xy + #define material_smoothness outlineRangeSmoothnessReserved.z +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 4 + #endif +) uniform Material { + MaterialUniform materials[MATERIAL_COUNT]; +}; +#endif + /* Textures */ #ifdef EXPLICIT_BINDING @@ -84,6 +132,14 @@ out lowp vec4 fragmentColor; #endif void main() { + #ifdef UNIFORM_BUFFERS + mediump const uint materialId = draws[drawOffset].draw_materialIdReserved & 0xffffu; + lowp const float smoothness = materials[materialId].material_smoothness; + lowp const vec4 color = materials[materialId].color; + lowp const vec4 outlineColor = materials[materialId].outlineColor; + lowp const vec2 outlineRange = materials[materialId].material_outlineRange; + #endif + lowp float intensity = texture(vectorTexture, interpolatedTextureCoordinates).r; /* Fill color */ diff --git a/src/Magnum/Shaders/DistanceFieldVector.h b/src/Magnum/Shaders/DistanceFieldVector.h index c3b15183c..dd5d665a8 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.h +++ b/src/Magnum/Shaders/DistanceFieldVector.h @@ -25,26 +25,227 @@ DEALINGS IN THE SOFTWARE. */ -#ifdef MAGNUM_BUILD_DEPRECATED /** @file - * @brief Typedef @ref Magnum::Shaders::DistanceFieldVector, alias @ref Magnum::Shaders::DistanceFieldVector2D, @ref Magnum::Shaders::DistanceFieldVector3D - * @m_deprecated_since_latest Use @ref Magnum/Shaders/DistanceFieldVectorGL.h, - * the @ref Magnum::Shaders::DistanceFieldVectorGL "DistanceFieldVectorGL" - * class and related typedefs instead. + * @brief Struct @ref Magnum::Shaders::DistanceFieldVectorDrawUniform, @ref Magnum::Shaders::DistanceFieldVectorMaterialUniform */ -#endif -#include "Magnum/configure.h" +#include "Magnum/Magnum.h" +#include "Magnum/Math/Color.h" #ifdef MAGNUM_BUILD_DEPRECATED #include #include "Magnum/Shaders/DistanceFieldVectorGL.h" - -CORRADE_DEPRECATED_FILE("use Magnum/Shaders/DistanceFieldVectorGL.h, the DistanceFieldVectorGL class and related typedefs instead") +#endif namespace Magnum { namespace Shaders { +/** +@brief Per-draw uniform for distance field vector shaders +@m_since_latest + +Together with the generic @ref TransformationProjectionUniform2D / +@ref TransformationProjectionUniform3D contains parameters that are specific to +each draw call. Texture transformation, if needed, is supplied separately in a +@ref TextureTransformationUniform; material-related properties are expected to +be shared among multiple draw calls and thus are provided in a separate +@ref DistanceFieldVectorMaterialUniform structure, referenced by +@ref materialId. +@see @ref DistanceFieldVectorGL::bindDrawBuffer() +*/ +struct DistanceFieldVectorDrawUniform { + /** @brief Construct with default parameters */ + constexpr explicit DistanceFieldVectorDrawUniform(DefaultInitT = DefaultInit) noexcept: materialId{0} {} + + /** @brief Construct without initializing the contents */ + explicit DistanceFieldVectorDrawUniform(NoInitT) noexcept {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref materialId field + * @return Reference to self (for method chaining) + */ + DistanceFieldVectorDrawUniform& setMaterialId(UnsignedInt id) { + materialId = id; + return *this; + } + + /** + * @} + */ + + /** @var materialId + * @brief Material ID + * + * References a particular material from a + * @ref DistanceFieldVectorMaterialUniform array. Useful when an UBO with + * more than one material is supplied or in a multi-draw scenario. Should + * be less than the material count passed to the + * @ref DistanceFieldVectorGL::DistanceFieldVectorGL(Flags, UnsignedInt, UnsignedInt) + * constructor. Default value is @cpp 0 @ce, meaning the first material + * gets used. + */ + + /* This field is an UnsignedInt in the shader and materialId is extracted + as (value & 0xffff), so the order has to be different on BE */ + #ifndef CORRADE_TARGET_BIG_ENDIAN + UnsignedShort materialId; + /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + UnsignedShort:16; /* reserved for skinOffset */ + #endif + #else + UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort materialId; + #endif + + /* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + Int:32; /* reserved for objectId */ + Int:32; + Int:32; + #endif +}; + +/** +@brief Material uniform for distance field vector shaders +@m_since_latest + +Describes material properties referenced from +@ref DistanceFieldVectorDrawUniform::materialId. +@see @ref DistanceFieldVectorGL::bindMaterialBuffer() +*/ +struct DistanceFieldVectorMaterialUniform { + /** @brief Construct with default parameters */ + constexpr explicit DistanceFieldVectorMaterialUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f}, outlineColor{0.0f, 0.0f, 0.0f, 0.0f}, outlineStart{0.5f}, outlineEnd{1.0f}, smoothness{0.04f} {} + + /** @brief Construct without initializing the contents */ + explicit DistanceFieldVectorMaterialUniform(NoInitT) noexcept: color{NoInit}, outlineColor{NoInit} {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref color field + * @return Reference to self (for method chaining) + */ + DistanceFieldVectorMaterialUniform& setColor(const Color4& color) { + this->color = color; + return *this; + } + + /** + * @brief Set the @ref outlineColor field + * @return Reference to self (for method chaining) + */ + DistanceFieldVectorMaterialUniform& setOutlineColor(const Color4& color) { + outlineColor = color; + return *this; + } + + /** + * @brief Set the @ref outlineStart and @ref outlineEnd fields + * @return Reference to self (for method chaining) + */ + DistanceFieldVectorMaterialUniform& setOutlineRange(Float start, Float end) { + outlineStart = start; + outlineEnd = end; + return *this; + } + + /** + * @brief Set the @ref smoothness field + * @return Reference to self (for method chaining) + */ + DistanceFieldVectorMaterialUniform& setSmoothness(Float smoothness) { + this->smoothness = smoothness; + return *this; + } + + /** + * @} + */ + + /** + * @brief Fill color + * + * Default value is @cpp 0xffffffff_rgbaf @ce. + * @see @ref DistanceFieldVectorGL::setColor() + */ + Color4 color; + + /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + Int:32; /* reserved for backgroundColor */ + Int:32; + Int:32; + Int:32; + #endif + + /** + * @brief Outline color + * + * Default value is @cpp 0x00000000_rgbaf @ce and the outline is not drawn + * --- see @ref outlineStart and @ref outlineEnd for more information. + * @see @ref DistanceFieldVectorGL::setColor() + */ + Color4 outlineColor; + + /** + * @brief Outline start + * + * Describe where fill ends and possible outline starts. Default value is + * @cpp 0.5f @ce, larger values will make the vector art look thinner, + * smaller will make it look thicker. + * @see @ref DistanceFieldVectorGL::setOutlineRange() + */ + Float outlineStart; + + /** + * @brief Outline end + * + * Describe where outline ends. If set to a value larger than + * @ref outlineStart, the outline is not drawn. Initial value is + * @cpp 1.0f @ce. + * @see @ref DistanceFieldVectorGL::setOutlineRange() + */ + Float outlineEnd; + + /** + * @brief Smoothness radius + * + * Larger values will make edges look less aliased (but blurry), smaller + * values will make them look more crisp (but possibly aliased). Initial + * value is @cpp 0.04f @ce. + * @see @ref DistanceFieldVectorGL::setSmoothness() + */ + Float smoothness; + + /* warning: Member __pad4__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + Int:32; + #endif +}; + +#ifdef MAGNUM_BUILD_DEPRECATED /** @brief @copybrief DistanceFieldVectorGL * @m_deprecated_since_latest Use @ref DistanceFieldVectorGL instead. */ @@ -61,10 +262,8 @@ typedef CORRADE_DEPRECATED("use DistanceFieldVectorGL2D instead") DistanceFieldV * @m_deprecated_since_latest Use @ref DistanceFieldVectorGL3D instead. */ typedef CORRADE_DEPRECATED("use DistanceFieldVectorGL3D instead") DistanceFieldVectorGL3D DistanceFieldVector3D; +#endif }} -#else -#error use Magnum/Shaders/DistanceFieldVectorGL.h, the DistanceFieldVectorGL class and related typedefs instead -#endif #endif diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp index 022195230..4ded3988b 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp @@ -37,15 +37,55 @@ #include "Magnum/Math/Matrix3.h" #include "Magnum/Math/Matrix4.h" +#ifndef MAGNUM_TARGET_GLES2 +#include + +#include "Magnum/GL/Buffer.h" +#endif + #include "Magnum/Shaders/Implementation/CreateCompatibilityShader.h" namespace Magnum { namespace Shaders { namespace { enum: Int { TextureUnit = 6 }; + + #ifndef MAGNUM_TARGET_GLES2 + enum: Int { + /* Not using the zero binding to avoid conflicts with + ProjectionBufferBinding from other shaders which can likely stay + bound to the same buffer for the whole time */ + TransformationProjectionBufferBinding = 1, + DrawBufferBinding = 2, + TextureTransformationBufferBinding = 3, + MaterialBufferBinding = 4 + }; + #endif } -template DistanceFieldVectorGL::DistanceFieldVectorGL(const Flags flags): _flags{flags} { +template DistanceFieldVectorGL::DistanceFieldVectorGL(const Flags flags + #ifndef MAGNUM_TARGET_GLES2 + , const UnsignedInt materialCount, const UnsignedInt drawCount + #endif +): + _flags{flags} + #ifndef MAGNUM_TARGET_GLES2 + , _materialCount{materialCount}, + _drawCount{drawCount} + #endif +{ + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || materialCount, + "Shaders::DistanceFieldVectorGL: material count can't be zero", ); + CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || drawCount, + "Shaders::DistanceFieldVectorGL: draw count can't be zero", ); + #endif + + #ifndef MAGNUM_TARGET_GLES + if(flags >= Flag::UniformBuffers) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); + #endif + #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ if(!Utility::Resource::hasGroup("MagnumShadersGL")) @@ -65,9 +105,27 @@ template DistanceFieldVectorGL::DistanceFiel GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment); vert.addSource(flags & Flag::TextureTransformation ? "#define TEXTURE_TRANSFORMATION\n" : "") - .addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n" : "#define THREE_DIMENSIONS\n") - .addSource(rs.get("generic.glsl")) + .addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n" : "#define THREE_DIMENSIONS\n"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + vert.addSource(Utility::formatString( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n", + drawCount)); + } + #endif + vert.addSource(rs.get("generic.glsl")) .addSource(rs.get("Vector.vert")); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + frag.addSource(Utility::formatString( + "#define UNIFORM_BUFFERS\n" + "#define MATERIAL_COUNT {}\n" + "#define DRAW_COUNT {}\n", + materialCount, + drawCount)); + } + #endif frag.addSource(rs.get("generic.glsl")) .addSource(rs.get("DistanceFieldVector.frag")); @@ -92,13 +150,20 @@ template DistanceFieldVectorGL::DistanceFiel if(!context.isExtensionSupported(version)) #endif { - _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); - if(flags & Flag::TextureTransformation) - _textureMatrixUniform = uniformLocation("textureMatrix"); - _colorUniform = uniformLocation("color"); - _outlineColorUniform = uniformLocation("outlineColor"); - _outlineRangeUniform = uniformLocation("outlineRange"); - _smoothnessUniform = uniformLocation("smoothness"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + _drawOffsetUniform = uniformLocation("drawOffset"); + } else + #endif + { + _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); + if(flags & Flag::TextureTransformation) + _textureMatrixUniform = uniformLocation("textureMatrix"); + _colorUniform = uniformLocation("color"); + _outlineColorUniform = uniformLocation("outlineColor"); + _outlineRangeUniform = uniformLocation("outlineRange"); + _smoothnessUniform = uniformLocation("smoothness"); + } } #ifndef MAGNUM_TARGET_GLES @@ -106,25 +171,54 @@ template DistanceFieldVectorGL::DistanceFiel #endif { setUniform(uniformLocation("vectorTexture"), TextureUnit); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + setUniformBlockBinding(uniformBlockIndex("TransformationProjection"), TransformationProjectionBufferBinding); + setUniformBlockBinding(uniformBlockIndex("Draw"), DrawBufferBinding); + setUniformBlockBinding(uniformBlockIndex("Material"), MaterialBufferBinding); + if(flags & Flag::TextureTransformation) + setUniformBlockBinding(uniformBlockIndex("TextureTransformation"), TextureTransformationBufferBinding); + } + #endif } /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ #ifdef MAGNUM_TARGET_GLES - setTransformationProjectionMatrix(MatrixTypeFor{Math::IdentityInit}); - if(flags & Flag::TextureTransformation) - setTextureMatrix(Matrix3{Math::IdentityInit}); - setColor(Color4{1.0f}); /* Outline color is zero by default */ - setOutlineRange(0.5f, 1.0f); - setSmoothness(0.04f); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + /* Draw offset is zero by default */ + } else + #endif + { + setTransformationProjectionMatrix(MatrixTypeFor{Math::IdentityInit}); + if(flags & Flag::TextureTransformation) + setTextureMatrix(Matrix3{Math::IdentityInit}); + setColor(Color4{1.0f}); + /* Outline color is zero by default */ + setOutlineRange(0.5f, 1.0f); + setSmoothness(0.04f); + } #endif } +#ifndef MAGNUM_TARGET_GLES2 +template DistanceFieldVectorGL::DistanceFieldVectorGL(const Flags flags): DistanceFieldVectorGL{flags, 1, 1} {} +#endif + template DistanceFieldVectorGL& DistanceFieldVectorGL::setTransformationProjectionMatrix(const MatrixTypeFor& matrix) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::DistanceFieldVectorGL::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_transformationProjectionMatrixUniform, matrix); return *this; } template DistanceFieldVectorGL& DistanceFieldVectorGL::setTextureMatrix(const Matrix3& matrix) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::DistanceFieldVectorGL::setTextureMatrix(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(_flags & Flag::TextureTransformation, "Shaders::DistanceFieldVectorGL::setTextureMatrix(): the shader was not created with texture transformation enabled", *this); setUniform(_textureMatrixUniform, matrix); @@ -132,25 +226,112 @@ template DistanceFieldVectorGL& DistanceFiel } template DistanceFieldVectorGL& DistanceFieldVectorGL::setColor(const Color4& color) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::DistanceFieldVectorGL::setColor(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_colorUniform, color); return *this; } template DistanceFieldVectorGL& DistanceFieldVectorGL::setOutlineColor(const Color4& color) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::DistanceFieldVectorGL::setOutlineColor(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_outlineColorUniform, color); return *this; } template DistanceFieldVectorGL& DistanceFieldVectorGL::setOutlineRange(Float start, Float end) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::DistanceFieldVectorGL::setOutlineRange(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_outlineRangeUniform, Vector2(start, end)); return *this; } template DistanceFieldVectorGL& DistanceFieldVectorGL::setSmoothness(Float value) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::DistanceFieldVectorGL::setSmoothness(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_smoothnessUniform, value); return *this; } +#ifndef MAGNUM_TARGET_GLES2 +template DistanceFieldVectorGL& DistanceFieldVectorGL::setDrawOffset(const UnsignedInt offset) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::DistanceFieldVectorGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(offset < _drawCount, + "Shaders::DistanceFieldVectorGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); + setUniform(_drawOffsetUniform, offset); + return *this; +} + +template DistanceFieldVectorGL& DistanceFieldVectorGL::bindTransformationProjectionBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::DistanceFieldVectorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); + return *this; +} + +template DistanceFieldVectorGL& DistanceFieldVectorGL::bindTransformationProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::DistanceFieldVectorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); + return *this; +} + +template DistanceFieldVectorGL& DistanceFieldVectorGL::bindDrawBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::DistanceFieldVectorGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding); + return *this; +} + +template DistanceFieldVectorGL& DistanceFieldVectorGL::bindDrawBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::DistanceFieldVectorGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); + return *this; +} + +template DistanceFieldVectorGL& DistanceFieldVectorGL::bindTextureTransformationBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::DistanceFieldVectorGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureTransformation, + "Shaders::DistanceFieldVectorGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding); + return *this; +} + +template DistanceFieldVectorGL& DistanceFieldVectorGL::bindTextureTransformationBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::DistanceFieldVectorGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureTransformation, + "Shaders::DistanceFieldVectorGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); + return *this; +} + +template DistanceFieldVectorGL& DistanceFieldVectorGL::bindMaterialBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::DistanceFieldVectorGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding); + return *this; +} + +template DistanceFieldVectorGL& DistanceFieldVectorGL::bindMaterialBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::DistanceFieldVectorGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); + return *this; +} +#endif + template DistanceFieldVectorGL& DistanceFieldVectorGL::bindVectorTexture(GL::Texture2D& texture) { texture.bind(TextureUnit); return *this; @@ -168,6 +349,9 @@ Debug& operator<<(Debug& debug, const DistanceFieldVectorGLFlag value) { /* LCOV_EXCL_START */ #define _c(v) case DistanceFieldVectorGLFlag::v: return debug << "::" #v; _c(TextureTransformation) + #ifndef MAGNUM_TARGET_GLES2 + _c(UniformBuffers) + #endif #undef _c /* LCOV_EXCL_STOP */ } @@ -177,8 +361,11 @@ Debug& operator<<(Debug& debug, const DistanceFieldVectorGLFlag value) { Debug& operator<<(Debug& debug, const DistanceFieldVectorGLFlags value) { return Containers::enumSetDebugOutput(debug, value, "Shaders::DistanceFieldVectorGL::Flags{}", { - DistanceFieldVectorGLFlag::TextureTransformation - }); + DistanceFieldVectorGLFlag::TextureTransformation, + #ifndef MAGNUM_TARGET_GLES2 + DistanceFieldVectorGLFlag::UniformBuffers + #endif + }); } } diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.h b/src/Magnum/Shaders/DistanceFieldVectorGL.h index 538cb8961..604c6106b 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.h +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.h @@ -39,7 +39,10 @@ namespace Magnum { namespace Shaders { namespace Implementation { enum class DistanceFieldVectorGLFlag: UnsignedByte { - TextureTransformation = 1 << 0 + TextureTransformation = 1 << 0, + #ifndef MAGNUM_TARGET_GLES2 + UniformBuffers = 1 << 1 + #endif }; typedef Containers::EnumSet DistanceFieldVectorGLFlags; } @@ -122,7 +125,23 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * @see @ref setTextureMatrix() * @m_since{2020,06} */ - TextureTransformation = 1 << 0 + TextureTransformation = 1 << 0, + + #ifndef MAGNUM_TARGET_GLES2 + /** + * Use uniform buffers. Expects that uniform data are supplied via + * @ref bindTransformationProjectionBuffer(), + * @ref bindDrawBuffer(), @ref bindTextureTransformationBuffer(), + * and @ref bindMaterialBuffer() instead of direct uniform setters. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES + * 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL + * 1.0. + * @m_since_latest + */ + UniformBuffers = 1 << 1 + #endif }; /** @@ -142,9 +161,50 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector /** * @brief Constructor * @param flags Flags + * + * While this function is meant mainly for the classic uniform + * scenario (without @ref Flag::UniformBuffers set), it's equivalent to + * @ref DistanceFieldVectorGL(Flags, UnsignedInt, UnsignedInt) with + * @p materialCount and @p drawCount set to @cpp 1 @ce. */ explicit DistanceFieldVectorGL(Flags flags = {}); + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Construct for a multi-draw scenario + * @param flags Flags + * @param materialCount Size of a @ref DistanceFieldVectorMaterialUniform + * buffer bound with @ref bindMaterialBuffer() + * @param drawCount Size of a @ref TransformationProjectionUniform2D + * / @ref TransformationProjectionUniform3D / + * @ref DistanceFieldVectorDrawUniform / + * @ref TextureTransformationUniform buffer bound with + * @ref bindTransformationProjectionBuffer(), @ref bindDrawBuffer() + * and @ref bindTextureTransformationBuffer() + * + * If @p flags contains @ref Flag::UniformBuffers, @p materialCount and + * @p drawCount describe the uniform buffer sizes as these are required + * to have a statically defined size. The draw offset is then set via + * @ref setDrawOffset() and the per-draw materials are specified via + * @ref DistanceFieldVectorDrawUniform::materialId. + * + * If @p flags don't contain @ref Flag::UniformBuffers, + * @p materialCount and @p drawCount is ignored and the constructor + * behaves the same as @ref DistanceFieldVectorGL(Flags). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + /** @todo this constructor will eventually need to have also joint + count, per-vertex weight count, view count for multiview and clip + plane count ... and putting them in arbitrary order next to each + other is too error-prone, so it needs some other solution + (accepting pairs of parameter type and value like in GL context + creation, e.g., which will probably need a new enum as reusing Flag + for this might be too confusing) */ + explicit DistanceFieldVectorGL(Flags flags, UnsignedInt materialCount, UnsignedInt drawCount); + #endif + /** * @brief Construct without creating the underlying OpenGL object * @@ -177,8 +237,40 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector */ Flags flags() const { return _flags; } + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Material count + * @m_since_latest + * + * Statically defined size of the + * @ref DistanceFieldVectorMaterialUniform uniform buffer. Has use only + * if @ref Flag::UniformBuffers is set. + * @see @ref bindMaterialBuffer() + * @requires_gles30 Not defined on OpenGL ES 2.0 builds. + * @requires_webgl20 Not defined on WebGL 1.0 builds. + */ + UnsignedInt materialCount() const { return _materialCount; } + + /** + * @brief Draw count + * @m_since_latest + * + * Statically defined size of each of the + * @ref TransformationProjectionUniform2D / + * @ref TransformationProjectionUniform3D, + * @ref DistanceFieldVectorDrawUniform and + * @ref TextureTransformationUniform uniform buffers. Has use only if + * @ref Flag::UniformBuffers is set. + * @requires_gles30 Not defined on OpenGL ES 2.0 builds. + * @requires_webgl20 Not defined on WebGL 1.0 builds. + */ + UnsignedInt drawCount() const { return _drawCount; } + #endif + /** @{ * @name Uniform setters + * + * Used only if @ref Flag::UniformBuffers is not set. */ /** @@ -186,6 +278,11 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * @return Reference to self (for method chaining) * * Initial value is an identity matrix. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref TransformationProjectionUniform2D::transformationProjectionMatrix / + * @ref TransformationProjectionUniform3D::transformationProjectionMatrix + * and call @ref bindTransformationProjectionBuffer() instead. */ DistanceFieldVectorGL& setTransformationProjectionMatrix(const MatrixTypeFor& matrix); @@ -197,6 +294,11 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * Expects that the shader was created with * @ref Flag::TextureTransformation enabled. Initial value is an * identity matrix. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref TextureTransformationUniform::rotationScaling and + * @ref TextureTransformationUniform::offset and call + * @ref bindTextureTransformationBuffer() instead. */ DistanceFieldVectorGL& setTextureMatrix(const Matrix3& matrix); @@ -205,6 +307,10 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * @return Reference to self (for method chaining) * * Initial value is @cpp 0xffffffff_rgbaf @ce. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref DistanceFieldVectorMaterialUniform::color and call + * @ref bindMaterialBuffer() instead. * @see @ref setOutlineColor() */ DistanceFieldVectorGL& setColor(const Color4& color); @@ -215,6 +321,10 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * * Initial value is @cpp 0x00000000_rgbaf @ce and the outline is not * drawn --- see @ref setOutlineRange() for more information. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref DistanceFieldVectorMaterialUniform::outlineColor and call + * @ref bindMaterialBuffer() instead. * @see @ref setOutlineRange(), @ref setColor() */ DistanceFieldVectorGL& setOutlineColor(const Color4& color); @@ -231,6 +341,10 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * larger than @p start, the outline is not drawn. Initial value is * @cpp 1.0f @ce. * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref DistanceFieldVectorMaterialUniform::outlineStart and + * @ref DistanceFieldVectorMaterialUniform::outlineEnd and call + * @ref bindMaterialBuffer() instead. * @see @ref setOutlineColor() */ DistanceFieldVectorGL& setOutlineRange(Float start, Float end); @@ -242,6 +356,10 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * Larger values will make edges look less aliased (but blurry), * smaller values will make them look more crisp (but possibly * aliased). Initial value is @cpp 0.04f @ce. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref DistanceFieldVectorMaterialUniform::smoothness and call + * @ref bindMaterialBuffer() instead. */ DistanceFieldVectorGL& setSmoothness(Float value); @@ -249,6 +367,120 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * @} */ + #ifndef MAGNUM_TARGET_GLES2 + /** @{ + * @name Uniform buffer binding and related uniform setters + * + * Used if @ref Flag::UniformBuffers is set. + */ + + /** + * @brief Set a draw offset + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Specifies which item in the @ref TransformationProjectionUniform2D / + * @ref TransformationProjectionUniform3D, + * @ref DistanceFieldVectorDrawUniform and + * @ref TextureTransformationUniform buffers bound with + * @ref bindTransformationProjectionBuffer(), @ref bindDrawBuffer() and + * @ref bindTextureTransformationBuffer() should be used for current + * draw. Expects that @ref Flag::UniformBuffers is set and @p offset is + * less than @ref drawCount(). Initial value is @cpp 0 @ce. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + DistanceFieldVectorGL& setDrawOffset(UnsignedInt offset); + + /** + * @brief Set a transformation and projection uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref drawCount() instances of + * @ref TransformationProjectionUniform2D / + * @ref TransformationProjectionUniform3D. At the very least you need + * to call also @ref bindDrawBuffer() and @ref bindMaterialBuffer(). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + DistanceFieldVectorGL& bindTransformationProjectionBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + DistanceFieldVectorGL& bindTransformationProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a draw uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref drawCount() instances of + * @ref DistanceFieldVectorDrawUniform. At the very least you need to + * call also @ref bindTransformationProjectionBuffer() and + * @ref bindMaterialBuffer(). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + DistanceFieldVectorGL& bindDrawBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + DistanceFieldVectorGL& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a texture transformation uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that both @ref Flag::UniformBuffers and + * @ref Flag::TextureTransformation is set. The buffer is expected to + * contain @ref drawCount() instances of + * @ref TextureTransformationUniform. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + DistanceFieldVectorGL& bindTextureTransformationBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + DistanceFieldVectorGL& bindTextureTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a material uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref materialCount() instances of + * @ref DistanceFieldVectorMaterialUniform. At the very least you need + * to call also @ref bindTransformationProjectionBuffer() and + * @ref bindDrawBuffer(). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + DistanceFieldVectorGL& bindMaterialBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + DistanceFieldVectorGL& bindMaterialBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @} + */ + #endif + /** @{ * @name Texture binding */ @@ -278,12 +510,20 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector #endif Flags _flags; + #ifndef MAGNUM_TARGET_GLES2 + UnsignedInt _materialCount{}, _drawCount{}; + #endif Int _transformationProjectionMatrixUniform{0}, _textureMatrixUniform{1}, _colorUniform{2}, _outlineColorUniform{3}, _outlineRangeUniform{4}, _smoothnessUniform{5}; + #ifndef MAGNUM_TARGET_GLES2 + /* Used instead of all other uniforms when Flag::UniformBuffers is set, + so it can alias them */ + Int _drawOffsetUniform{0}; + #endif }; /** diff --git a/src/Magnum/Shaders/Flat.frag b/src/Magnum/Shaders/Flat.frag index 995b2bacb..613f84b05 100644 --- a/src/Magnum/Shaders/Flat.frag +++ b/src/Magnum/Shaders/Flat.frag @@ -27,14 +27,23 @@ #extension GL_EXT_gpu_shader4: require #endif +#if defined(UNIFORM_BUFFERS) && defined(ALPHA_MASK) && !defined(GL_ES) +#extension GL_ARB_shader_bit_encoding: require +#endif + #ifndef NEW_GLSL #define fragmentColor gl_FragColor #define texture texture2D #define in varying #endif +#ifndef RUNTIME_CONST +#define const +#endif + /* Uniforms */ +#ifndef UNIFORM_BUFFERS #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 2) #endif @@ -63,6 +72,34 @@ layout(location = 4) uniform highp uint objectId; /* defaults to zero */ #endif +/* Uniform buffers */ + +#else +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 0) +#endif +uniform highp uint drawOffset + #ifndef GL_ES + = 0u + #endif + ; + +struct DrawUniform { + lowp vec4 color; + highp uvec4 objectIdReservedAlphaMaskReserved; + #define draw_objectId objectIdReservedAlphaMaskReserved.x + #define draw_alphaMask objectIdReservedAlphaMaskReserved.z +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 2 + #endif +) uniform Draw { + DrawUniform draws[DRAW_COUNT]; +}; +#endif + /* Textures */ #ifdef TEXTURED @@ -103,6 +140,16 @@ out highp uint fragmentObjectId; #endif void main() { + #ifdef UNIFORM_BUFFERS + lowp const vec4 color = draws[drawOffset].color; + #ifdef OBJECT_ID + highp const uint objectId = draws[drawOffset].draw_objectId; + #endif + #ifdef ALPHA_MASK + lowp const float alphaMask = uintBitsToFloat(draws[drawOffset].draw_alphaMask); + #endif + #endif + fragmentColor = #ifdef TEXTURED texture(textureData, interpolatedTextureCoordinates)* diff --git a/src/Magnum/Shaders/Flat.h b/src/Magnum/Shaders/Flat.h index 70311e465..f11ea91e1 100644 --- a/src/Magnum/Shaders/Flat.h +++ b/src/Magnum/Shaders/Flat.h @@ -25,26 +25,136 @@ DEALINGS IN THE SOFTWARE. */ -#ifdef MAGNUM_BUILD_DEPRECATED /** @file - * @brief Typedef @ref Magnum::Shaders::Flat, alias @ref Magnum::Shaders::Flat2D, @ref Magnum::Shaders::Flat3D - * @m_deprecated_since_latest Use @ref Magnum/Shaders/FlatGL.h, the - * @ref Magnum::Shaders::FlatGL "FlatGL" class and - * related typedefs instead. + * @brief Struct @ref Magnum::Shaders::FlatDrawUniform */ -#endif -#include "Magnum/configure.h" +#include "Magnum/Magnum.h" +#include "Magnum/Math/Color.h" #ifdef MAGNUM_BUILD_DEPRECATED #include #include "Magnum/Shaders/FlatGL.h" - -CORRADE_DEPRECATED_FILE("use Magnum/Shaders/FlatGL.h, the FlatGL class and related typedefs instead") +#endif namespace Magnum { namespace Shaders { +/** +@brief Per-draw uniform for flat shaders +@m_since_latest + +Together with the generic @ref TransformationProjectionUniform2D / +@ref TransformationProjectionUniform3D contains parameters that are specific to +each draw call. Texture transformation, if needed, is supplied separately in a +@ref TextureTransformationUniform. +@see @ref FlatGL::bindDrawBuffer() +*/ +struct FlatDrawUniform { + /** @brief Construct with default parameters */ + constexpr explicit FlatDrawUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f}, objectId{0}, alphaMask{0.5f} {} + + /** @brief Construct without initializing the contents */ + explicit FlatDrawUniform(NoInitT) noexcept: color{NoInit} {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref objectId field + * @return Reference to self (for method chaining) + */ + FlatDrawUniform& setObjectId(UnsignedInt id) { + objectId = id; + return *this; + } + + /** + * @brief Set the @ref alphaMask field + * @return Reference to self (for method chaining) + */ + FlatDrawUniform& setAlphaMask(Float alphaMask) { + this->alphaMask = alphaMask; + return *this; + } + + /** + * @brief Set the @ref color field + * @return Reference to self (for method chaining) + */ + FlatDrawUniform& setColor(const Color4& color) { + this->color = color; + return *this; + } + + /** + * @} + */ + + /** + * @brief Color + * + * Default value is @cpp 0xffffffff_rgbaf @ce. + * + * If @ref FlatGL::Flag::VertexColor is enabled, the color is multiplied + * with a color coming from the @ref FlatGL::Color3 / @ref FlatGL::Color4 + * attribute. + * @see @ref FlatGL::setColor() + */ + Color4 color; + + /** + * @brief Object ID + * + * Used only for the object ID framebuffer output, not to access any other + * uniform data. Default value is @cpp 0 @ce. + * + * Used only if @ref FlatGL::Flag::ObjectId is enabled, ignored otherwise. + * If @ref FlatGL::Flag::InstancedObjectId is enabled as well, this value + * is added to the ID coming from the @ref FlatGL::ObjectId attribute. + * @see @ref FlatGL::setObjectId() + */ + UnsignedInt objectId; + + /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + /* This field is an UnsignedInt in the shader and skinOffset is extracted + as (value >> 16), so the order has to be different on BE */ + #ifndef CORRADE_TARGET_BIG_ENDIAN + UnsignedShort:16; + UnsignedShort:16; /* reserved for skinOffset */ + #else + UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort:16; + #endif + #endif + + /** + * @brief Alpha mask value + * + * Fragments with alpha values smaller than the mask value will be + * discarded. Default value is @cpp 0.5f @ce. + * + * Used only if @ref FlatGL::Flag::AlphaMask is enabled, ignored otherwise. + * @see @ref FlatGL::setAlphaMask() + */ + Float alphaMask; + + /* warning: Member __pad2__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + Int:32; + #endif +}; + +#ifdef MAGNUM_BUILD_DEPRECATED /** @brief @copybrief FlatGL * @m_deprecated_since_latest Use @ref FlatGL instead. */ @@ -61,10 +171,8 @@ typedef CORRADE_DEPRECATED("use FlatGL2D instead") FlatGL2D Flat2D; * @m_deprecated_since_latest Use @ref FlatGL3D instead. */ typedef CORRADE_DEPRECATED("use FlatGL3D instead") FlatGL3D Flat3D; +#endif }} -#else -#error use Magnum/Shaders/FlatGL.h, the FlatGL class and related typedefs instead -#endif #endif diff --git a/src/Magnum/Shaders/Flat.vert b/src/Magnum/Shaders/Flat.vert index 1c4305fc6..93fa99e1b 100644 --- a/src/Magnum/Shaders/Flat.vert +++ b/src/Magnum/Shaders/Flat.vert @@ -32,8 +32,13 @@ #define out varying #endif +#ifndef RUNTIME_CONST +#define const +#endif + /* Uniforms */ +#ifndef UNIFORM_BUFFERS #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -64,6 +69,51 @@ uniform mediump mat3 textureMatrix ; #endif +/* Uniform buffers */ + +#else +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 0) +#endif +uniform highp uint drawOffset + #ifndef GL_ES + = 0u + #endif + ; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 1 + #endif +) uniform TransformationProjection { + highp + #ifdef TWO_DIMENSIONS + mat3 + #elif defined(THREE_DIMENSIONS) + mat4 + #else + #error + #endif + transformationProjectionMatrices[DRAW_COUNT]; +}; + +#ifdef TEXTURE_TRANSFORMATION +struct TextureTransformationUniform { + highp vec4 rotationScaling; + highp vec4 offsetReservedReserved; + #define textureTransformation_offset offsetReservedReserved.xy +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 3 + #endif +) uniform TextureTransformation { + TextureTransformationUniform textureTransformations[DRAW_COUNT]; +}; +#endif +#endif + /* Inputs */ #ifdef EXPLICIT_ATTRIB_LOCATION @@ -133,6 +183,21 @@ flat out highp uint interpolatedInstanceObjectId; #endif void main() { + #ifdef UNIFORM_BUFFERS + highp const + #ifdef TWO_DIMENSIONS + mat3 + #elif defined(THREE_DIMENSIONS) + mat4 + #else + #error + #endif + transformationProjectionMatrix = transformationProjectionMatrices[drawOffset]; + #ifdef TEXTURE_TRANSFORMATION + mediump const mat3 textureMatrix = mat3(textureTransformations[drawOffset].rotationScaling.xy, 0.0, textureTransformations[drawOffset].rotationScaling.zw, 0.0, textureTransformations[drawOffset].textureTransformation_offset, 1.0); + #endif + #endif + #ifdef TWO_DIMENSIONS gl_Position.xywz = vec4(transformationProjectionMatrix* #ifdef INSTANCED_TRANSFORMATION diff --git a/src/Magnum/Shaders/FlatGL.cpp b/src/Magnum/Shaders/FlatGL.cpp index cb054eaf9..5887f781c 100644 --- a/src/Magnum/Shaders/FlatGL.cpp +++ b/src/Magnum/Shaders/FlatGL.cpp @@ -39,16 +39,52 @@ #include "Magnum/Shaders/Implementation/CreateCompatibilityShader.h" +#ifndef MAGNUM_TARGET_GLES2 +#include + +#include "Magnum/GL/Buffer.h" +#endif + namespace Magnum { namespace Shaders { namespace { enum: Int { TextureUnit = 0 }; + + #ifndef MAGNUM_TARGET_GLES2 + enum: Int { + /* Not using the zero binding to avoid conflicts with + ProjectionBufferBinding from other shaders which can likely stay + bound to the same buffer for the whole time */ + TransformationProjectionBufferBinding = 1, + DrawBufferBinding = 2, + TextureTransformationBufferBinding = 3 + }; + #endif } -template FlatGL::FlatGL(const Flags flags): _flags(flags) { +template FlatGL::FlatGL(const Flags flags + #ifndef MAGNUM_TARGET_GLES2 + , const UnsignedInt drawCount + #endif +): + _flags{flags} + #ifndef MAGNUM_TARGET_GLES2 + , _drawCount{drawCount} + #endif +{ CORRADE_ASSERT(!(flags & Flag::TextureTransformation) || (flags & Flag::Textured), "Shaders::FlatGL: texture transformation enabled but the shader is not textured", ); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || drawCount, + "Shaders::FlatGL: draw count can't be zero", ); + #endif + + #ifndef MAGNUM_TARGET_GLES + if(flags >= Flag::UniformBuffers) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); + #endif + #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ if(!Utility::Resource::hasGroup("MagnumShadersGL")) @@ -75,8 +111,16 @@ template FlatGL::FlatGL(const Flags flags): .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(flags >= Flag::InstancedTextureOffset ? "#define INSTANCED_TEXTURE_OFFSET\n" : ""); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + vert.addSource(Utility::formatString( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n", + drawCount)); + } + #endif + vert.addSource(rs.get("generic.glsl")) .addSource(rs.get("Flat.vert")); frag.addSource(flags & Flag::Textured ? "#define TEXTURED\n" : "") .addSource(flags & Flag::AlphaMask ? "#define ALPHA_MASK\n" : "") @@ -85,7 +129,16 @@ template FlatGL::FlatGL(const Flags flags): .addSource(flags & Flag::ObjectId ? "#define OBJECT_ID\n" : "") .addSource(flags >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") #endif - .addSource(rs.get("generic.glsl")) + ; + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + frag.addSource(Utility::formatString( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n", + drawCount)); + } + #endif + frag.addSource(rs.get("generic.glsl")) .addSource(rs.get("Flat.frag")); CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag})); @@ -125,14 +178,21 @@ template FlatGL::FlatGL(const Flags flags): if(!context.isExtensionSupported(version)) #endif { - _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); - if(flags & Flag::TextureTransformation) - _textureMatrixUniform = uniformLocation("textureMatrix"); - _colorUniform = uniformLocation("color"); - if(flags & Flag::AlphaMask) _alphaMaskUniform = uniformLocation("alphaMask"); #ifndef MAGNUM_TARGET_GLES2 - if(flags & Flag::ObjectId) _objectIdUniform = uniformLocation("objectId"); + if(flags >= Flag::UniformBuffers) { + _drawOffsetUniform = uniformLocation("drawOffset"); + } else #endif + { + _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); + if(flags & Flag::TextureTransformation) + _textureMatrixUniform = uniformLocation("textureMatrix"); + _colorUniform = uniformLocation("color"); + if(flags & Flag::AlphaMask) _alphaMaskUniform = uniformLocation("alphaMask"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags & Flag::ObjectId) _objectIdUniform = uniformLocation("objectId"); + #endif + } } #ifndef MAGNUM_TARGET_GLES @@ -140,25 +200,52 @@ template FlatGL::FlatGL(const Flags flags): #endif { if(flags & Flag::Textured) setUniform(uniformLocation("textureData"), TextureUnit); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + setUniformBlockBinding(uniformBlockIndex("TransformationProjection"), TransformationProjectionBufferBinding); + setUniformBlockBinding(uniformBlockIndex("Draw"), DrawBufferBinding); + if(flags & Flag::TextureTransformation) + setUniformBlockBinding(uniformBlockIndex("TextureTransformation"), TextureTransformationBufferBinding); + } + #endif } /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ #ifdef MAGNUM_TARGET_GLES - setTransformationProjectionMatrix(MatrixTypeFor{Math::IdentityInit}); - if(flags & Flag::TextureTransformation) - setTextureMatrix(Matrix3{Math::IdentityInit}); - setColor(Magnum::Color4{1.0f}); - if(flags & Flag::AlphaMask) setAlphaMask(0.5f); - /* Object ID is zero by default */ + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + /* Draw offset is zero by default */ + } else + #endif + { + setTransformationProjectionMatrix(MatrixTypeFor{Math::IdentityInit}); + if(flags & Flag::TextureTransformation) + setTextureMatrix(Matrix3{Math::IdentityInit}); + setColor(Magnum::Color4{1.0f}); + if(flags & Flag::AlphaMask) setAlphaMask(0.5f); + /* Object ID is zero by default */ + } #endif } +#ifndef MAGNUM_TARGET_GLES2 +template FlatGL::FlatGL(const Flags flags): FlatGL{flags, 1} {} +#endif + template FlatGL& FlatGL::setTransformationProjectionMatrix(const MatrixTypeFor& matrix) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::FlatGL::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_transformationProjectionMatrixUniform, matrix); return *this; } template FlatGL& FlatGL::setTextureMatrix(const Matrix3& matrix) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::FlatGL::setTextureMatrix(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(_flags & Flag::TextureTransformation, "Shaders::FlatGL::setTextureMatrix(): the shader was not created with texture transformation enabled", *this); setUniform(_textureMatrixUniform, matrix); @@ -166,11 +253,19 @@ template FlatGL& FlatGL::setText } template FlatGL& FlatGL::setColor(const Magnum::Color4& color) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::FlatGL::setColor(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_colorUniform, color); return *this; } template FlatGL& FlatGL::setAlphaMask(Float mask) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::FlatGL::setAlphaMask(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(_flags & Flag::AlphaMask, "Shaders::FlatGL::setAlphaMask(): the shader was not created with alpha mask enabled", *this); setUniform(_alphaMaskUniform, mask); @@ -179,6 +274,8 @@ template FlatGL& FlatGL::setAlph #ifndef MAGNUM_TARGET_GLES2 template FlatGL& FlatGL::setObjectId(UnsignedInt id) { + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::FlatGL::setObjectId(): the shader was created with uniform buffers enabled", *this); CORRADE_ASSERT(_flags & Flag::ObjectId, "Shaders::FlatGL::setObjectId(): the shader was not created with object ID enabled", *this); setUniform(_objectIdUniform, id); @@ -186,6 +283,63 @@ template FlatGL& FlatGL::setObje } #endif +#ifndef MAGNUM_TARGET_GLES2 +template FlatGL& FlatGL::setDrawOffset(const UnsignedInt offset) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::FlatGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(offset < _drawCount, + "Shaders::FlatGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); + setUniform(_drawOffsetUniform, offset); + return *this; +} + +template FlatGL& FlatGL::bindTransformationProjectionBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::FlatGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); + return *this; +} + +template FlatGL& FlatGL::bindTransformationProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::FlatGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); + return *this; +} + +template FlatGL& FlatGL::bindDrawBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::FlatGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding); + return *this; +} + +template FlatGL& FlatGL::bindDrawBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::FlatGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); + return *this; +} + +template FlatGL& FlatGL::bindTextureTransformationBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::FlatGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureTransformation, + "Shaders::FlatGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding); + return *this; +} + +template FlatGL& FlatGL::bindTextureTransformationBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::FlatGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureTransformation, + "Shaders::FlatGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); + return *this; +} +#endif + template FlatGL& FlatGL::bindTexture(GL::Texture2D& texture) { CORRADE_ASSERT(_flags & Flag::Textured, "Shaders::FlatGL::bindTexture(): the shader was not created with texturing enabled", *this); @@ -214,11 +368,14 @@ Debug& operator<<(Debug& debug, const FlatGLFlag value) { #endif _c(InstancedTransformation) _c(InstancedTextureOffset) + #ifndef MAGNUM_TARGET_GLES2 + _c(UniformBuffers) + #endif #undef _c /* LCOV_EXCL_STOP */ } - return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedShort(value)) << Debug::nospace << ")"; } Debug& operator<<(Debug& debug, const FlatGLFlags value) { @@ -232,7 +389,11 @@ Debug& operator<<(Debug& debug, const FlatGLFlags value) { FlatGLFlag::InstancedObjectId, /* Superset of ObjectId */ FlatGLFlag::ObjectId, #endif - FlatGLFlag::InstancedTransformation}); + FlatGLFlag::InstancedTransformation, + #ifndef MAGNUM_TARGET_GLES2 + FlatGLFlag::UniformBuffers + #endif + }); } } diff --git a/src/Magnum/Shaders/FlatGL.h b/src/Magnum/Shaders/FlatGL.h index 24db89982..d052ca5e7 100644 --- a/src/Magnum/Shaders/FlatGL.h +++ b/src/Magnum/Shaders/FlatGL.h @@ -38,7 +38,7 @@ namespace Magnum { namespace Shaders { namespace Implementation { - enum class FlatGLFlag: UnsignedByte { + enum class FlatGLFlag: UnsignedShort { Textured = 1 << 0, AlphaMask = 1 << 1, VertexColor = 1 << 2, @@ -48,7 +48,10 @@ namespace Implementation { InstancedObjectId = (1 << 5)|ObjectId, #endif InstancedTransformation = 1 << 6, - InstancedTextureOffset = (1 << 7)|TextureTransformation + InstancedTextureOffset = (1 << 7)|TextureTransformation, + #ifndef MAGNUM_TARGET_GLES2 + UniformBuffers = 1 << 8 + #endif }; typedef Containers::EnumSet FlatGLFlags; } @@ -269,7 +272,7 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * * @see @ref Flags, @ref flags() */ - enum class Flag: UnsignedByte { + enum class Flag: UnsignedShort { /** * Multiply color with a texture. * @see @ref setColor(), @ref bindTexture() @@ -319,9 +322,10 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: /** * Instanced object ID. Retrieves a per-instance / per-vertex * object ID from the @ref ObjectId attribute, outputting a sum of - * the per-vertex ID and ID coming from @ref setObjectId(). - * Implicitly enables @ref Flag::ObjectId. See - * @ref Shaders-FlatGL-object-id for more information. + * the per-vertex ID and ID coming from @ref setObjectId() or + * @ref FlatDrawUniform::objectId. Implicitly enables + * @ref Flag::ObjectId. See @ref Shaders-FlatGL-object-id for more + * information. * @requires_gl30 Extension @gl_extension{EXT,gpu_shader4} * @requires_gles30 Object ID output requires integer support in * shaders, which is not available in OpenGL ES 2.0 or WebGL @@ -335,8 +339,10 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * 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 setTransformationProjectionMatrix() or + * @ref TransformationProjectionUniform2D::transformationProjectionMatrix + * / @ref TransformationProjectionUniform3D::transformationProjectionMatrix + * (first the per-instance, then the uniform matrix). See * @ref Shaders-FlatGL-instancing for more information. * @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} * @requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, @@ -351,7 +357,9 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: /** * 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 + * the matrix coming from @ref setTextureMatrix() or + * @ref TextureTransformationUniform::rotationScaling and + * @ref TextureTransformationUniform::offset (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(). @@ -365,7 +373,23 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * in WebGL 1.0. * @m_since{2020,06} */ - InstancedTextureOffset = (1 << 7)|TextureTransformation + InstancedTextureOffset = (1 << 7)|TextureTransformation, + + #ifndef MAGNUM_TARGET_GLES2 + /** + * Use uniform buffers. Expects that uniform data are supplied via + * @ref bindTransformationProjectionBuffer(), + * @ref bindDrawBuffer() and @ref bindTextureTransformationBuffer() + * instead of direct uniform setters. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES + * 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL + * 1.0. + * @m_since_latest + */ + UniformBuffers = 1 << 8 + #endif }; /** @@ -384,9 +408,37 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: /** * @brief Constructor * @param flags Flags + * + * While this function is meant mainly for the classic uniform + * scenario (without @ref Flag::UniformBuffers set), it's equivalent to + * @ref FlatGL(Flags, UnsignedInt) with @p drawCount set to @cpp 1 @ce. */ explicit FlatGL(Flags flags = {}); + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Construct for a multi-draw scenario + * @param flags Flags + * @param drawCount Size of a @ref TransformationProjectionUniform2D / + * @ref TransformationProjectionUniform3D / @ref FlatDrawUniform / + * @ref TextureTransformationUniform buffer bound with + * @ref bindTransformationProjectionBuffer(), @ref bindDrawBuffer() + * and @ref bindTextureTransformationBuffer() + * + * If @p flags contains @ref Flag::UniformBuffers, @p drawCount + * describes the uniform buffer sizes as these are required to have a + * statically defined size. The draw offset is then set via + * @ref setDrawOffset(). + * + * If @p flags don't contain @ref Flag::UniformBuffers, @p drawCount is + * ignored and the constructor behaves the same as @ref FlatGL(Flags). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + explicit FlatGL(Flags flags, UnsignedInt drawCount); + #endif + /** * @brief Construct without creating the underlying OpenGL object * @@ -416,8 +468,26 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: /** @brief Flags */ Flags flags() const { return _flags; } + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Draw count + * @m_since_latest + * + * Statically defined size of each of the + * @ref TransformationProjectionUniform2D / + * @ref TransformationProjectionUniform3D, @ref FlatDrawUniform and + * @ref TextureTransformationUniform uniform buffers. Has use only if + * @ref Flag::UniformBuffers is set. + * @requires_gles30 Not defined on OpenGL ES 2.0 builds. + * @requires_webgl20 Not defined on WebGL 1.0 builds. + */ + UnsignedInt drawCount() const { return _drawCount; } + #endif + /** @{ * @name Uniform setters + * + * Used only if @ref Flag::UniformBuffers is not set. */ /** @@ -428,6 +498,11 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * @ref Flag::InstancedTransformation is set, the per-instance * transformation matrix coming from the @ref TransformationMatrix * attribute is applied first, before this one. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref TransformationProjectionUniform2D::transformationProjectionMatrix / + * @ref TransformationProjectionUniform3D::transformationProjectionMatrix + * and call @ref bindTransformationProjectionBuffer() instead. */ FlatGL& setTransformationProjectionMatrix(const MatrixTypeFor& matrix); @@ -441,6 +516,11 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * identity matrix. If @ref Flag::InstancedTextureOffset is set, the * per-instance offset coming from the @ref TextureOffset attribute is * applied first, before this matrix. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref TextureTransformationUniform::rotationScaling and + * @ref TextureTransformationUniform::offset and call + * @ref bindTextureTransformationBuffer() instead. */ FlatGL& setTextureMatrix(const Matrix3& matrix); @@ -452,6 +532,9 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * is set, the color is multiplied with the texture. If * @ref Flag::VertexColor is set, the color is multiplied with a color * coming from the @ref Color3 / @ref Color4 attribute. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref FlatDrawUniform::color and call @ref bindDrawBuffer() instead. * @see @ref bindTexture() */ FlatGL& setColor(const Magnum::Color4& color); @@ -467,6 +550,10 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * * This corresponds to @m_class{m-doc-external} [glAlphaFunc()](https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glAlphaFunc.xml) * in classic OpenGL. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref FlatDrawUniform::alphaMask and call @ref bindDrawBuffer() + * instead. * @m_keywords{glAlphaFunc()} */ FlatGL& setAlphaMask(Float mask); @@ -481,6 +568,10 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * @ref Shaders-FlatGL-object-id for more information. Default is * @cpp 0 @ce. If @ref Flag::InstancedObjectId is enabled as well, this * value is added to the ID coming from the @ref ObjectId attribute. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref FlatDrawUniform::objectId and call @ref bindDrawBuffer() + * instead. * @requires_gl30 Extension @gl_extension{EXT,gpu_shader4} * @requires_gles30 Object ID output requires integer support in * shaders, which is not available in OpenGL ES 2.0 or WebGL 1.0. @@ -492,6 +583,97 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * @} */ + #ifndef MAGNUM_TARGET_GLES2 + /** @{ + * @name Uniform buffer binding and related uniform setters + * + * Used if @ref Flag::UniformBuffers is set. + */ + + /** + * @brief Set a draw offset + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Specifies which item in the @ref TransformationProjectionUniform2D / + * @ref TransformationProjectionUniform3D, @ref FlatDrawUniform and + * @ref TextureTransformationUniform buffers bound with + * @ref bindTransformationProjectionBuffer(), @ref bindDrawBuffer() and + * @ref bindTextureTransformationBuffer() should be used for current + * draw. Expects that @ref Flag::UniformBuffers is set and @p offset is + * less than @ref drawCount(). Initial value is @cpp 0 @ce. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + FlatGL& setDrawOffset(UnsignedInt offset); + + /** + * @brief Set a transformation and projection uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref drawCount() instances of + * @ref TransformationProjectionUniform2D / + * @ref TransformationProjectionUniform3D. At the very least you need + * to call also @ref bindDrawBuffer(). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + FlatGL& bindTransformationProjectionBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + FlatGL& bindTransformationProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a draw uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref drawCount() instances of + * @ref FlatDrawUniform. At the very least you need to call also + * @ref bindTransformationProjectionBuffer(). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + FlatGL& bindDrawBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + FlatGL& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a texture transformation uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that both @ref Flag::UniformBuffers and + * @ref Flag::TextureTransformation is set. The buffer is expected to + * contain @ref drawCount() instances of + * @ref TextureTransformationUniform. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + FlatGL& bindTextureTransformationBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + FlatGL& bindTextureTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @} + */ + #endif + /** @{ * @name Texture binding */ @@ -521,12 +703,18 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: #endif Flags _flags; + #ifndef MAGNUM_TARGET_GLES2 + UnsignedInt _drawCount{}; + #endif Int _transformationProjectionMatrixUniform{0}, _textureMatrixUniform{1}, _colorUniform{2}, _alphaMaskUniform{3}; #ifndef MAGNUM_TARGET_GLES2 Int _objectIdUniform{4}; + /* Used instead of all other uniforms when Flag::UniformBuffers is set, + so it can alias them */ + Int _drawOffsetUniform{0}; #endif }; diff --git a/src/Magnum/Shaders/Generic.h b/src/Magnum/Shaders/Generic.h index e588298ec..c7f73a340 100644 --- a/src/Magnum/Shaders/Generic.h +++ b/src/Magnum/Shaders/Generic.h @@ -25,32 +25,442 @@ DEALINGS IN THE SOFTWARE. */ -#ifdef MAGNUM_BUILD_DEPRECATED /** @file - * @brief Typedef @ref Magnum::Shaders::Generic, alias @ref Magnum::Shaders::Generic2D, @ref Magnum::Shaders::Generic3D - * @m_deprecated_since_latest Use @ref Magnum/Shaders/GenericGL.h, the - * @ref Magnum::Shaders::GenericGL "GenericGL" class and - * related typedefs instead. + * @brief Struct @ref Magnum::Shaders::ProjectionUniform2D, @ref Magnum::Shaders::ProjectionUniform3D, @ref Magnum::Shaders::TransformationUniform2D, @ref Magnum::Shaders::TransformationUniform3D, @ref Magnum::Shaders::TextureTransformationUniform */ -#endif -#include "Magnum/configure.h" +#include "Magnum/Magnum.h" +#include "Magnum/Math/Matrix3.h" +#include "Magnum/Math/Matrix4.h" #ifdef MAGNUM_BUILD_DEPRECATED #include #include "Magnum/Shaders/GenericGL.h" +#endif + +namespace Magnum { namespace Shaders { + +/** +@brief 2D projection uniform common for all shaders +@m_since_latest + +Contains the per-view projection matrix. +*/ +struct ProjectionUniform2D { + /** @brief Construct with default parameters */ + constexpr explicit ProjectionUniform2D(DefaultInitT = DefaultInit) noexcept: projectionMatrix{Math::IdentityInit} {} + /** @brief Construct without initializing the contents */ + explicit ProjectionUniform2D(NoInitT) noexcept: projectionMatrix{NoInit} {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref projectionMatrix field + * @return Reference to self (for method chaining) + * + * The matrix is expanded to @relativeref{Magnum,Matrix3x4}, with the + * bottom row being zeros. + */ + ProjectionUniform2D& setProjectionMatrix(const Matrix3& matrix) { + projectionMatrix = Matrix3x4{matrix}; + return *this; + } + + /** + * @} + */ + + /** + * @brief Projection matrix + * + * Default value is an identity matrix (i.e., an orthographic projection of + * the default @f$ [ -\boldsymbol{1} ; \boldsymbol{1} ] @f$ cube). The + * bottom row is unused and acts only as a padding to match uniform buffer + * packing rules. + */ + Matrix3x4 projectionMatrix; +}; + +/** +@brief 3D projection uniform common for all shaders +@m_since_latest + +Contains the per-view projection matrix used by the @ref MeshVisualizerGL3D +and @ref PhongGL shaders that need a separate projection and transformation +matrix. +@see @ref MeshVisualizerGL3D::bindProjectionBuffer(), + @ref PhongGL::bindProjectionBuffer() +*/ +struct ProjectionUniform3D { + /** @brief Construct with default parameters */ + constexpr explicit ProjectionUniform3D(DefaultInitT = DefaultInit) noexcept: projectionMatrix{Math::IdentityInit} {} + /** @brief Construct without initializing the contents */ + explicit ProjectionUniform3D(NoInitT) noexcept: projectionMatrix{NoInit} {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref projectionMatrix field + * @return Reference to self (for method chaining) + */ + ProjectionUniform3D& setProjectionMatrix(const Matrix4& matrix) { + projectionMatrix = matrix; + return *this; + } + + /** + * @} + */ + + /** + * @brief Projection matrix + * + * Default value is an identity matrix (i.e., an orthographic projection of + * the default @f$ [ -\boldsymbol{1} ; \boldsymbol{1} ] @f$ cube). + * @see @ref PhongGL::setProjectionMatrix() + */ + Matrix4 projectionMatrix; +}; + +/** +@brief 2D transformation uniform common for all shaders +@m_since_latest + +Contains the per-draw transformation matrix. +*/ +struct TransformationUniform2D { + /** @brief Construct with default parameters */ + constexpr explicit TransformationUniform2D(DefaultInitT = DefaultInit) noexcept: transformationMatrix{Math::IdentityInit} {} + /** @brief Construct without initializing the contents */ + explicit TransformationUniform2D(NoInitT) noexcept: transformationMatrix{NoInit} {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref transformationMatrix field + * @return Reference to self (for method chaining) + * + * The matrix is expanded to @relativeref{Magnum,Matrix3x4}, with the + * bottom row being zeros. + */ + TransformationUniform2D& setTransformationMatrix(const Matrix3& matrix) { + transformationMatrix = Matrix3x4{matrix}; + return *this; + } + + /** + * @} + */ + + /** + * @brief Transformation matrix + * + * Default value is an identity matrix. The bottom row is unused and acts + * only as a padding to match uniform buffer packing rules. + */ + Matrix3x4 transformationMatrix; +}; + +/** +@brief 3D transformation uniform common for all shaders +@m_since_latest + +Contains the per-draw transformation matrix used by the @ref MeshVisualizerGL3D +and @ref PhongGL shaders that need a separate projection and transformation +matrix. +@see @ref MeshVisualizerGL3D::bindTransformationBuffer(), + @ref PhongGL::bindTransformationBuffer() +*/ +struct TransformationUniform3D { + /** @brief Construct with default parameters */ + constexpr explicit TransformationUniform3D(DefaultInitT = DefaultInit) noexcept: transformationMatrix{Math::IdentityInit} {} + /** @brief Construct without initializing the contents */ + explicit TransformationUniform3D(NoInitT) noexcept: transformationMatrix{NoInit} {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ -CORRADE_DEPRECATED_FILE("use Magnum/Shaders/GenericGL.h, the GenericGL class and related typedefs instead") + /** + * @brief Set the @ref transformationMatrix field + * @return Reference to self (for method chaining) + */ + TransformationUniform3D& setTransformationMatrix(const Matrix4& matrix) { + transformationMatrix = matrix; + return *this; + } + /** + * @} + */ + + /** + * @brief Transformation matrix + * + * Default value is an identity matrix. + * + * If @ref PhongGL::Flag::InstancedTransformation is enabled, the + * per-instance transformation coming from the + * @ref PhongGL::TransformationMatrix attribute is applied first, before + * this one. + * @see @ref PhongGL::setTransformationMatrix(), + * @ref MeshVisualizerGL3D::setTransformationMatrix() + */ + Matrix4 transformationMatrix; +}; + +/** +@brief Combined 2D projection and transformation uniform common for all shaders +@m_since_latest + +Used by @ref FlatGL, @ref MeshVisualizerGL2D, @ref DistanceFieldVectorGL, +@ref VectorGL and @ref VertexColorGL that don't need to have a separate +projection matrix supplied. +@see @ref DistanceFieldVectorGL2D::bindTransformationProjectionBuffer(), + @ref FlatGL2D::bindTransformationProjectionBuffer(), + @ref MeshVisualizerGL2D::bindTransformationProjectionBuffer(), + @ref VectorGL2D::bindTransformationProjectionBuffer(), + @ref VertexColorGL2D::bindTransformationProjectionBuffer() +*/ +struct TransformationProjectionUniform2D { + /** @brief Construct with default parameters */ + constexpr explicit TransformationProjectionUniform2D(DefaultInitT = DefaultInit) noexcept: transformationProjectionMatrix{Math::IdentityInit} {} + /** @brief Construct without initializing the contents */ + explicit TransformationProjectionUniform2D(NoInitT) noexcept: transformationProjectionMatrix{NoInit} {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref transformationProjectionMatrix field + * @return Reference to self (for method chaining) + * + * The matrix is expanded to @relativeref{Magnum,Matrix3x4}, with the + * bottom row being zeros. + */ + TransformationProjectionUniform2D& setTransformationProjectionMatrix(const Matrix3& matrix) { + transformationProjectionMatrix = Matrix3x4{matrix}; + return *this; + } + + /** + * @} + */ + + /** + * @brief Transformation and projection matrix + * + * Default value is an identity matrix. The bottom row is unused and acts + * only as a padding to match uniform buffer packing rules. + * + * If @ref FlatGL::Flag::InstancedTransformation is enabled, the + * per-instance transformation coming from the + * @ref FlatGL::TransformationMatrix attribute is applied first, before + * this one. + * @see @ref DistanceFieldVectorGL2D::setTransformationProjectionMatrix(), + * @ref FlatGL2D::setTransformationProjectionMatrix(), + * @ref MeshVisualizerGL2D::setTransformationProjectionMatrix(), + * @ref VectorGL2D::setTransformationProjectionMatrix(), + * @ref VertexColorGL2D::setTransformationProjectionMatrix() + */ + Matrix3x4 transformationProjectionMatrix; +}; + +/** +@brief Combined 3D projection and transformation uniform common for all shaders +@m_since_latest + +Used by @ref FlatGL, @ref DistanceFieldVectorGL, @ref VectorGL and +@ref VertexColorGL that don't need to have a separate projection matrix +supplied. +@see @ref DistanceFieldVectorGL3D::bindTransformationProjectionBuffer(), + @ref FlatGL3D::bindTransformationProjectionBuffer(), + @ref VectorGL3D::bindTransformationProjectionBuffer(), + @ref VertexColorGL3D::bindTransformationProjectionBuffer() +*/ +struct TransformationProjectionUniform3D { + /** @brief Construct with default parameters */ + constexpr explicit TransformationProjectionUniform3D(DefaultInitT = DefaultInit) noexcept: transformationProjectionMatrix{Math::IdentityInit} {} + /** @brief Construct without initializing the contents */ + explicit TransformationProjectionUniform3D(NoInitT) noexcept: transformationProjectionMatrix{NoInit} {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref transformationProjectionMatrix field + * @return Reference to self (for method chaining) + */ + TransformationProjectionUniform3D& setTransformationProjectionMatrix(const Matrix4& matrix) { + transformationProjectionMatrix = matrix; + return *this; + } + + /** + * @} + */ + + /** + * @brief Transformation and projection matrix + * + * Default value is an identity matrix. + * + * If @ref FlatGL::Flag::InstancedTransformation is enabled, the + * per-instance transformation coming from the + * @ref FlatGL::TransformationMatrix attribute is applied first, before + * this one. + * @see @ref DistanceFieldVectorGL3D::setTransformationProjectionMatrix(), + * @ref FlatGL3D::setTransformationProjectionMatrix(), + * @ref VectorGL3D::setTransformationProjectionMatrix(), + * @ref VertexColorGL3D::setTransformationProjectionMatrix() + */ + Matrix4 transformationProjectionMatrix; +}; + +/** +@brief Texture transformation uniform common for all shaders +@m_since_latest + +Expands upon @ref TransformationUniform2D / @ref TransformationUniform3D with +texture-related parameters. + +Used only if @ref DistanceFieldVectorGL::Flag::TextureTransformation, +@ref FlatGL::Flag::TextureTransformation, +@ref PhongGL::Flag::TextureTransformation or +@ref VectorGL::Flag::TextureTransformation is enabled. +@see @ref DistanceFieldVectorGL::bindTextureTransformationBuffer(), + @ref FlatGL::bindTextureTransformationBuffer(), + @ref PhongGL::bindTextureTransformationBuffer(), + @ref VectorGL::bindTextureTransformationBuffer() +*/ +struct TextureTransformationUniform { + /** @brief Construct with default parameters */ + constexpr explicit TextureTransformationUniform(DefaultInitT = DefaultInit) noexcept: rotationScaling{Math::IdentityInit} {} + /** @brief Construct without initializing the contents */ + explicit TextureTransformationUniform(NoInitT) noexcept: rotationScaling{NoInit}, offset{NoInit} {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref rotationScaling and @ref offset fields + * @return Reference to self (for method chaining) + * + * The @ref rotationScaling field is set to the upper left 2x2 corner of + * @p transformation, @ref offset to the two upper elements of the + * rightmost column of @p transformation. Bottom row is ignored, as it's + * expected to be always @cpp {0.0f, 0.0f, 1.0f} @ce. + */ + TextureTransformationUniform& setTextureMatrix(const Matrix3& transformation) { + rotationScaling = transformation.rotationScaling(); + offset = transformation.translation(); + return *this; + } + + /** + * @} + */ + + /** + * @brief Texture rotation and scaling + * + * The top left part of a 3x3 texture transformation matrix. The + * transformation is split between @ref rotationScaling and @ref offset to + * make it occupy just two @cb{.glsl} vec4 @ce slots in the uniform buffer + * instead of three. Default value is an identity matrix. + * + * If @ref FlatGL::Flag::InstancedTextureOffset / + * @ref PhongGL::Flag::InstancedTextureOffset is enabled, the + * per-instance offset coming from the @ref FlatGL::TextureOffset / + * @ref PhongGL::TextureOffset attribute is applied first, before + * this transformation. + * @see @ref DistanceFieldVectorGL::setTextureMatrix(), + * @ref FlatGL::setTextureMatrix(), @ref PhongGL::setTextureMatrix(), + * @ref VectorGL::setTextureMatrix() + */ + Matrix2x2 rotationScaling; + + /** + * @brief Texture offset + * + * Top two elements of the rightmost column of a 3x3 texture transformation + * matrix. The transformation is split between @ref rotationScaling and + * @ref offset to make it occupy just two @cb{.glsl} vec4 @ce slots in the + * uniform buffer instead of three. Default value is a zero vector. + * + * If @ref FlatGL::Flag::InstancedTextureOffset / + * @ref PhongGL::Flag::InstancedTextureOffset is enabled, the + * per-instance offset coming from the @ref FlatGL::TextureOffset / + * @ref PhongGL::TextureOffset attribute is applied first, before + * this transformation. + * @see @ref DistanceFieldVectorGL::setTextureMatrix(), + * @ref FlatGL::setTextureMatrix(), @ref PhongGL::setTextureMatrix(), + * @ref VectorGL::setTextureMatrix() + */ + Vector2 offset; + + /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + Int:32; /* reserved for layer */ + Int:32; /* reserved for coordinateSet */ + #endif +}; + +#ifdef MAGNUM_BUILD_DEPRECATED /* Deprecated aliases not present here but in GenericGL.h instead, as a lot of existing code relies on these being transitively included from Phong.h etc., and there are no forward declarations in Shaders.h as the type is never used like that. While we *could* include Generic.h from Phong.h, we'd have to also temporarily disable the CORRADE_DEPRECATED_FILE() macro there and it's more pain than it's worth. */ -#else -#error use Magnum/Shaders/GenericGL.h, the GenericGL class and related typedefs instead #endif +}} + #endif diff --git a/src/Magnum/Shaders/MeshVisualizer.frag b/src/Magnum/Shaders/MeshVisualizer.frag index 7b7fb5e0b..e75b64eea 100644 --- a/src/Magnum/Shaders/MeshVisualizer.frag +++ b/src/Magnum/Shaders/MeshVisualizer.frag @@ -46,6 +46,7 @@ /* Uniforms */ +#ifndef UNIFORM_BUFFERS #if (defined(WIREFRAME_RENDERING) || defined(INSTANCED_OBJECT_ID) || defined(VERTEX_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID)) && !defined(TBN_DIRECTION) #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 1) @@ -110,6 +111,64 @@ uniform lowp vec2 colorMapOffsetScale #define colorMapScale colorMapOffsetScale.y #endif +/* Uniform buffers */ + +#else +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 0) +#endif +uniform highp uint drawOffset + #ifndef GL_ES + = 0u + #endif + ; + +/* Keep in sync with MeshVisualizer.vert and MeshVisualizer.geom. Can't + "outsource" to a common file because the extension directives need to be + always before any code. */ +struct DrawUniform { + #ifdef THREE_DIMENSIONS + highp mat3 normalMatrix; /* actually mat3x4 */ + #elif !defined(TWO_DIMENSIONS) + #error + #endif + highp uvec4 materialIdReservedReservedReservedReserved; + #define draw_materialIdReserved materialIdReservedReservedReservedReserved.x +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 2 + #endif +) uniform Draw { + DrawUniform draws[DRAW_COUNT]; +}; + +/* Keep in sync with MeshVisualizer.vert and MeshVisualizer.geom. Can't + "outsource" to a common file because the extension directives need to be + always before any code. */ +struct MaterialUniform { + lowp vec4 color; + lowp vec4 wireframeColor; + lowp vec4 wireframeWidthColorMapOffsetColorMapScaleLineWidth; + #define material_wireframeWidth wireframeWidthColorMapOffsetColorMapScaleLineWidth.x + #define material_colorMapOffset wireframeWidthColorMapOffsetColorMapScaleLineWidth.y + #define material_colorMapScale wireframeWidthColorMapOffsetColorMapScaleLineWidth.z + #define material_lineWidth wireframeWidthColorMapOffsetColorMapScaleLineWidth.w + lowp vec4 lineLengthSmoothnessReservedReserved; + #define material_lineLength lineLengthSmoothnessReservedReserved.x + #define material_smoothness lineLengthSmoothnessReservedReserved.y +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 4 + #endif +) uniform Material { + MaterialUniform materials[MATERIAL_COUNT]; +}; +#endif + /* Textures */ #if defined(INSTANCED_OBJECT_ID) || defined(VERTEX_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID) @@ -157,6 +216,26 @@ out lowp vec4 fragmentColor; #endif void main() { + #ifdef UNIFORM_BUFFERS + mediump const uint materialId = draws[drawOffset].draw_materialIdReserved & 0xffffu; + #if (defined(WIREFRAME_RENDERING) || defined(INSTANCED_OBJECT_ID) || defined(VERTEX_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID)) && !defined(TBN_DIRECTION) + lowp const vec4 color = materials[materialId].color; + lowp const vec4 wireframeColor = materials[materialId].wireframeColor; + #endif + #ifdef WIREFRAME_RENDERING + lowp const float wireframeWidth = materials[materialId].material_wireframeWidth; + #elif defined(TBN_DIRECTION) + lowp const float lineWidth = materials[materialId].material_lineWidth; + #endif + #if defined(WIREFRAME_RENDERING) || defined(TBN_DIRECTION) + lowp const float smoothness = materials[materialId].material_smoothness; + #endif + #if defined(INSTANCED_OBJECT_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID) + lowp const float colorMapOffset = materials[materialId].material_colorMapOffset; + lowp const float colorMapScale = materials[materialId].material_colorMapScale; + #endif + #endif + /* Map object/vertex/primitive ID to a color. Will be either combined with the wireframe background color (if wireframe is enabled), ignored (if rendering TBN direction) or used as-is if nothing else is enabled */ diff --git a/src/Magnum/Shaders/MeshVisualizer.geom b/src/Magnum/Shaders/MeshVisualizer.geom index e609692d2..928e1f2b2 100644 --- a/src/Magnum/Shaders/MeshVisualizer.geom +++ b/src/Magnum/Shaders/MeshVisualizer.geom @@ -36,12 +36,14 @@ /* Uniforms */ +/* This one is for both classic and UBOs, as it's usually set globally instead + of changing per-draw */ #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 5) #endif uniform lowp vec2 viewportSize; /* defaults to zero */ - +#ifndef UNIFORM_BUFFERS #if (defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION)) && (defined(WIREFRAME_RENDERING) || defined(INSTANCED_OBJECT_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID)) #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 1) @@ -82,6 +84,64 @@ uniform lowp float smoothness ; #endif +/* Uniform buffers */ + +#else +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 0) +#endif +uniform highp uint drawOffset + #ifndef GL_ES + = 0u + #endif + ; + +/* Keep in sync with MeshVisualizer.vert and MeshVisualizer.frag. Can't + "outsource" to a common file because the #extension directives need to be + always before any code. */ +struct DrawUniform { + #ifdef THREE_DIMENSIONS + highp mat3 normalMatrix; /* actually mat3x4 */ + #elif !defined(TWO_DIMENSIONS) + #error + #endif + highp uvec4 materialIdReservedReservedReservedReserved; + #define draw_materialIdReserved materialIdReservedReservedReservedReserved.x +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 2 + #endif +) uniform Draw { + DrawUniform draws[DRAW_COUNT]; +}; + +/* Keep in sync with MeshVisualizer.vert and MeshVisualizer.frag. Can't + "outsource" to a common file because the #extension directives need to be + always before any code. */ +struct MaterialUniform { + lowp vec4 color; + lowp vec4 wireframeColor; + lowp vec4 wireframeWidthColorMapOffsetColorMapScaleLineWidth; + #define material_wireframeWidth wireframeWidthColorMapOffsetColorMapScaleLineWidth.x + #define material_colorMapOffset wireframeWidthColorMapOffsetColorMapScaleLineWidth.y + #define material_colorMapScale wireframeWidthColorMapOffsetColorMapScaleLineWidth.z + #define material_lineWidth wireframeWidthColorMapOffsetColorMapScaleLineWidth.w + lowp vec4 lineLengthSmoothnessReservedReserved; + #define material_lineLength lineLengthSmoothnessReservedReserved.x + #define material_smoothness lineLengthSmoothnessReservedReserved.y +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 4 + #endif +) uniform Material { + MaterialUniform materials[MATERIAL_COUNT]; +}; +#endif + /* Inputs */ layout(triangles) in; @@ -130,7 +190,17 @@ flat out highp uint interpolatedPrimitiveId; out lowp vec4 backgroundColor; out lowp vec4 lineColor; -void emitQuad(vec4 position, vec2 positionScreen, vec4 endpoint, vec2 endpointScreen) { +void emitQuad( + #ifdef UNIFORM_BUFFERS + uint materialId, + #endif + vec4 position, vec2 positionScreen, vec4 endpoint, vec2 endpointScreen +) { + #if defined(UNIFORM_BUFFERS) && (defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION)) + lowp const float lineWidth = materials[materialId].material_lineWidth; + lowp const float smoothness = materials[materialId].material_smoothness; + #endif + /* Calculate screen-space locations for the bar vertices and form two triangles out of them. In case TBN is rendered alone, half bar width is lineWidth + smoothness to allow for antialiasing, in case it's rendered @@ -184,6 +254,18 @@ void emitQuad(vec4 position, vec2 positionScreen, vec4 endpoint, vec2 endpointSc #endif void main() { + #ifdef UNIFORM_BUFFERS + mediump const uint materialId = draws[drawOffset].draw_materialIdReserved & 0xffffu; + #if (defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION)) && (defined(WIREFRAME_RENDERING) || defined(INSTANCED_OBJECT_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID)) + lowp const vec4 color = materials[materialId].color; + lowp const vec4 wireframeColor = materials[materialId].wireframeColor; + #endif + #if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION) + lowp const float lineWidth = materials[materialId].material_lineWidth; + lowp const float smoothness = materials[materialId].material_smoothness; + #endif + #endif + /* Passthrough for unchanged variables */ #ifdef INSTANCED_OBJECT_ID interpolatedInstanceObjectId = interpolatedVsInstanceObjectId[0]; @@ -262,17 +344,32 @@ void main() { for(int i = 0; i != 3; ++i) { #ifdef TANGENT_DIRECTION lineColor = vec4(1.0, 0.0, 0.0, 1.0); - emitQuad(gl_in[i].gl_Position, p[i], tangentEndpoint[i], t[i]); + emitQuad( + #ifdef UNIFORM_BUFFERS + materialId, + #endif + gl_in[i].gl_Position, p[i], tangentEndpoint[i], t[i] + ); #endif #ifdef BITANGENT_DIRECTION lineColor = vec4(0.0, 1.0, 0.0, 1.0); - emitQuad(gl_in[i].gl_Position, p[i], bitangentEndpoint[i], b[i]); + emitQuad( + #ifdef UNIFORM_BUFFERS + materialId, + #endif + gl_in[i].gl_Position, p[i], bitangentEndpoint[i], b[i] + ); #endif #ifdef NORMAL_DIRECTION lineColor = vec4(0.0, 0.0, 1.0, 1.0); - emitQuad(gl_in[i].gl_Position, p[i], normalEndpoint[i], n[i]); + emitQuad( + #ifdef UNIFORM_BUFFERS + materialId, + #endif + gl_in[i].gl_Position, p[i], normalEndpoint[i], n[i] + ); #endif } #endif diff --git a/src/Magnum/Shaders/MeshVisualizer.h b/src/Magnum/Shaders/MeshVisualizer.h index 3a725bdef..85463592c 100644 --- a/src/Magnum/Shaders/MeshVisualizer.h +++ b/src/Magnum/Shaders/MeshVisualizer.h @@ -25,27 +25,419 @@ DEALINGS IN THE SOFTWARE. */ -#ifdef MAGNUM_BUILD_DEPRECATED /** @file - * @brief Typedef @ref Magnum::Shaders::MeshVisualizer2D, @ref Magnum::Shaders::MeshVisualizer3D - * @m_deprecated_since_latest Use @ref Magnum/Shaders/MeshVisualizerGL.h and - * the @ref Magnum::Shaders::MeshVisualizerGL2D "MeshVisualizerGL2D" / - * @ref Magnum::Shaders::MeshVisualizerGL3D "MeshVisualizerGL3D" class - * instead + * @brief Struct @ref Magnum::Shaders::MeshVisualizerDrawUniform2D, @ref Magnum::Shaders::MeshVisualizerDrawUniform3D, @ref Magnum::Shaders::MeshVisualizerMaterialUniform */ -#endif -#include "Magnum/configure.h" +#include "Magnum/Magnum.h" +#include "Magnum/Math/Color.h" +#include "Magnum/Math/Matrix.h" #ifdef MAGNUM_BUILD_DEPRECATED #include #include "Magnum/Shaders/MeshVisualizerGL.h" - -CORRADE_DEPRECATED_FILE("use Magnum/Shaders/MeshVisualizerGL.h and the MeshVisualizerGL2D / MeshVisualizer3D class instead") +#endif namespace Magnum { namespace Shaders { +/** +@brief Per-draw uniform for 2D mesh visualizer shaders +@m_since_latest + +Together with the generic @ref TransformationProjectionUniform2D contains +parameters that are specific to each draw call. Material-related properties are +expected to be shared among multiple draw calls and thus are provided in a +separate @ref MeshVisualizerMaterialUniform structure, referenced by +@ref materialId. +@see @ref MeshVisualizerGL2D::bindDrawBuffer() +*/ +struct MeshVisualizerDrawUniform2D { + /** @brief Construct with default parameters */ + constexpr explicit MeshVisualizerDrawUniform2D(DefaultInitT = DefaultInit) noexcept: materialId{0} {} + + /** @brief Construct without initializing the contents */ + explicit MeshVisualizerDrawUniform2D(NoInitT) noexcept {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref materialId field + * @return Reference to self (for method chaining) + */ + MeshVisualizerDrawUniform2D& setMaterialId(UnsignedInt id) { + materialId = id; + return *this; + } + + /** + * @} + */ + + /** @var materialId + * @brief Material ID + * + * References a particular material from a + * @ref MeshVisualizerMaterialUniform array. Useful when an UBO with + * more than one material is supplied or in a multi-draw scenario. Should + * be less than the material count passed to the @ref MeshVisualizerGL2D::MeshVisualizerGL2D(Flags, UnsignedInt, UnsignedInt) + * / @ref MeshVisualizerGL3D::MeshVisualizerGL3D(Flags, UnsignedInt, UnsignedInt) + * constructor. Default value is @cpp 0 @ce, meaning the first material + * gets used. + */ + + /* This field is an UnsignedInt in the shader and materialId is extracted + as (value & 0xffff), so the order has to be different on BE */ + #ifndef CORRADE_TARGET_BIG_ENDIAN + UnsignedShort materialId; + /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + UnsignedShort:16; /* reserved for skinOffset */ + #endif + #else + UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort materialId; + #endif + + /* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + Int:32; + Int:32; + Int:32; + #endif +}; + +/** +@brief Per-draw uniform for 3D mesh visualizer shaders +@m_since_latest + +Together with the generic @ref TransformationUniform3D contains parameters that +are specific to each draw call. Material-related properties are expected to be +shared among multiple draw calls and thus are provided in a separate +@ref MeshVisualizerMaterialUniform structure, referenced by @ref materialId. +@see @ref MeshVisualizerGL3D::bindDrawBuffer() +*/ +struct MeshVisualizerDrawUniform3D { + /** @brief Construct with default parameters */ + constexpr explicit MeshVisualizerDrawUniform3D(DefaultInitT = DefaultInit) noexcept: normalMatrix{Math::IdentityInit}, materialId{0} {} + + /** @brief Construct without initializing the contents */ + explicit MeshVisualizerDrawUniform3D(NoInitT) noexcept: normalMatrix{NoInit} {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref normalMatrix field + * @return Reference to self (for method chaining) + * + * The matrix is expanded to @relativeref{Magnum,Matrix3x4}, with the + * bottom row being zeros. + */ + MeshVisualizerDrawUniform3D& setNormalMatrix(const Matrix3x3& matrix) { + normalMatrix = Matrix3x4{matrix}; + return *this; + } + + /** + * @brief Set the @ref materialId field + * @return Reference to self (for method chaining) + */ + MeshVisualizerDrawUniform3D& setMaterialId(UnsignedInt id) { + materialId = id; + return *this; + } + + /** + * @} + */ + + /** + * @brief Normal matrix + * + * Default value is an identity matrix. The bottom row is unused and acts + * only as a padding to match uniform buffer packing rules. + * @see @ref MeshVisualizerGL3D::setNormalMatrix() + */ + Matrix3x4 normalMatrix; + + /** @var materialId + * @brief Material ID + * + * References a particular material from a + * @ref MeshVisualizerMaterialUniform array. Useful when an UBO with + * more than one material is supplied or in a multi-draw scenario. Should + * be less than the material count passed to @ref MeshVisualizerGL2D / + * @ref MeshVisualizerGL3D constructor. Default value is @cpp 0 @ce, + * meaning the first material gets used. + */ + + /* This field is an UnsignedInt in the shader and materialId is extracted + as (value & 0xffff), so the order has to be different on BE */ + #ifndef CORRADE_TARGET_BIG_ENDIAN + UnsignedShort materialId; + /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + UnsignedShort:16; /* reserved for skinOffset */ + #endif + #else + UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort materialId; + #endif + + /* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + Int:32; + Int:32; + Int:32; + #endif +}; + +/** +@brief Material uniform for mesh visualizer shaders +@m_since_latest + +Describes material properties referenced from +@ref MeshVisualizerDrawUniform2D::materialId and +@ref MeshVisualizerDrawUniform3D::materialId. +*/ +struct MeshVisualizerMaterialUniform { + /** @brief Construct with default parameters */ + constexpr explicit MeshVisualizerMaterialUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f}, wireframeColor{0.0f, 0.0f, 0.0f, 1.0f}, wireframeWidth{1.0f}, colorMapOffset{1.0f/512.0f}, colorMapScale{1.0f/256.0f}, lineWidth{1.0f}, lineLength{1.0f}, smoothness{2.0f} {} + + /** @brief Construct without initializing the contents */ + explicit MeshVisualizerMaterialUniform(NoInitT) noexcept: color{NoInit}, wireframeColor{NoInit} {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref color field + * @return Reference to self (for method chaining) + */ + MeshVisualizerMaterialUniform& setColor(const Color4& color) { + this->color = color; + return *this; + } + + /** + * @brief Set the @ref wireframeColor field + * @return Reference to self (for method chaining) + */ + MeshVisualizerMaterialUniform& setWireframeColor(const Color4& color) { + wireframeColor = color; + return *this; + } + + /** + * @brief Set the @ref wireframeWidth field + * @return Reference to self (for method chaining) + */ + MeshVisualizerMaterialUniform& setWireframeWidth(Float width) { + wireframeWidth = width; + return *this; + } + + /** + * @brief Set the @ref colorMapOffset and @ref colorMapScale fields + * @return Reference to self (for method chaining) + */ + MeshVisualizerMaterialUniform& setColorMapTransformation(Float offset, Float scale) { + colorMapOffset = offset; + colorMapScale = scale; + return *this; + } + + /** + * @brief Set the @ref lineWidth field + * @return Reference to self (for method chaining) + */ + MeshVisualizerMaterialUniform& setLineWidth(Float width) { + lineWidth = width; + return *this; + } + + /** + * @brief Set the @ref lineLength field + * @return Reference to self (for method chaining) + */ + MeshVisualizerMaterialUniform& setLineLength(Float length) { + lineLength = length; + return *this; + } + + /** + * @brief Set the @ref smoothness field + * @return Reference to self (for method chaining) + */ + MeshVisualizerMaterialUniform& setSmoothness(Float smoothness) { + this->smoothness = smoothness; + return *this; + } + + /** + * @} + */ + + /** + * @brief Base object color + * + * Default value is @cpp 0xffffffff_rgbaf @ce. + * + * Used only if @ref MeshVisualizerGL3D::Flag::Wireframe "MeshVisualizerGL*D::Flag::Wireframe" + * or @relativeref{MeshVisualizerGL3D,Flag::InstancedObjectId} / + * @relativeref{MeshVisualizerGL3D,Flag::PrimitiveId} / + * @relativeref{MeshVisualizerGL3D,Flag::PrimitiveIdFromVertexId} is + * enabled. In case of the latter, the color is multiplied with the color + * map coming from @ref MeshVisualizerGL3D::bindColorMapTexture() "MeshVisualizerGL*D::bindColorMapTexture()". + * @see @ref MeshVisualizerGL2D::setColor(), + * @ref MeshVisualizerGL3D::setColor() + */ + Color4 color; + + /** + * @brief Wireframe color + * + * Default value is @cpp 0x000000ff_rgbaf @ce. + * + * Used only if @ref MeshVisualizerGL3D::Flag::Wireframe "MeshVisualizerGL*D::Flag::Wireframe" + * is enabled. + * @see @ref MeshVisualizerGL2D::setWireframeColor(), + * @ref MeshVisualizerGL3D::setWireframeColor() + */ + Color4 wireframeColor; + + /** + * @brief Wireframe width + * + * The value is in screen space (depending on + * @ref MeshVisualizerGL3D::setViewportSize() "MeshVisualizerGL*D::setViewportSize()"), + * default value is @cpp 1.0f @ce. + * + * Used only if @ref MeshVisualizerGL3D::Flag::Wireframe "MeshVisualizerGL*D::Flag::Wireframe" + * is enabled. + * @see @ref MeshVisualizerGL2D::setWireframeWidth(), + * @ref MeshVisualizerGL3D::setWireframeWidth() + */ + Float wireframeWidth; + + /** + * @brief Color map offset + * + * Together with @ref colorMapScale forms an offset and scale applied to + * the input value coming either from the + * @ref MeshVisualizerGL3D::ObjectId "MeshVisualizerGL*D::ObjectId" + * attribute or @glsl gl_PrimitiveID @ce, resulting value is then used to + * fetch a color from a color map bound with @ref MeshVisualizerGL3D::bindColorMapTexture() "MeshVisualizerGL*D::bindColorMapTexture()". + * Default offset and scale values are @cpp 1.0f/512.0f @ce and + * @cpp 1.0/256.0f @ce, meaning that for a 256-entry colormap the first 256 + * values get an exact color from it and the next values will be either + * clamped to last color or repeated depending on the color map texture + * wrapping mode. + * + * Used only if @ref MeshVisualizerGL3D::Flag::InstancedObjectId "MeshVisualizerGL*D::Flag::InstancedObjectId" or + * @relativeref{MeshVisualizerGL3D,Flag::PrimitiveId} / + * @relativeref{MeshVisualizerGL3D,Flag::PrimitiveIdFromVertexId} is + * enabled. + * @see @ref MeshVisualizerGL2D::setColorMapTransformation(), + * @ref MeshVisualizerGL3D::setColorMapTransformation() + */ + Float colorMapOffset; + + /** + * @brief Color map offset + * + * See @ref colorMapOffset for more information. + * + * Used only by @ref MeshVisualizerGL3D and only if + * @ref MeshVisualizerGL3D::Flag::InstancedObjectId "MeshVisualizerGL*D::Flag::InstancedObjectId" or + * @relativeref{MeshVisualizerGL3D,Flag::PrimitiveId} / + * @relativeref{MeshVisualizerGL3D,Flag::PrimitiveIdFromVertexId} is + * enabled. + * @see @ref MeshVisualizerGL2D::setColorMapTransformation(), + * @ref MeshVisualizerGL3D::setColorMapTransformation() + */ + Float colorMapScale; + + /** + * @brief Line width + * + * The value is in screen space (depending on + * @ref MeshVisualizerGL3D::setViewportSize() "MeshVisualizerGL*D::setViewportSize()"), + * default value is @cpp 1.0f @ce. + * + * Used only by @ref MeshVisualizerGL3D and only if + * @ref MeshVisualizerGL3D::Flag::TangentDirection, + * @relativeref{MeshVisualizerGL3D,Flag::BitangentFromTangentDirection}, + * @relativeref{MeshVisualizerGL3D,Flag::BitangentDirection} or + * @relativeref{MeshVisualizerGL3D,Flag::NormalDirection} is enabled. + * @see @ref MeshVisualizerGL3D::setLineWidth() + */ + Float lineWidth; + + /** + * @brief Line length + * + * The value is in object space, default value is @cpp 1.0f @ce. + * + * Used only by @ref MeshVisualizerGL3D and only if + * @ref MeshVisualizerGL3D::Flag::TangentDirection, + * @relativeref{MeshVisualizerGL3D,Flag::BitangentFromTangentDirection}, + * @relativeref{MeshVisualizerGL3D,Flag::BitangentDirection} or + * @relativeref{MeshVisualizerGL3D,Flag::NormalDirection} is enabled. + * @see @ref MeshVisualizerGL3D::setLineLength() + */ + Float lineLength; + + /** + * @brief Line smoothness + * + * The value is in screen space (depending on + * @ref MeshVisualizerGL3D::setViewportSize() "MeshVisualizerGL*D::setViewportSize()"), + * initial value is @cpp 2.0f @ce. + * + * Used only if @ref MeshVisualizerGL3D::Flag::Wireframe "MeshVisualizerGL*D::Flag::Wireframe", + * @ref MeshVisualizerGL3D::Flag::TangentDirection, + * @relativeref{MeshVisualizerGL3D,Flag::BitangentFromTangentDirection}, + * @relativeref{MeshVisualizerGL3D,Flag::BitangentDirection} or + * @relativeref{MeshVisualizerGL3D,Flag::NormalDirection} is enabled. + * @see @ref MeshVisualizerGL2D::setSmoothness(), + * @ref MeshVisualizerGL3D::setSmoothness() + */ + Float smoothness; + + /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + Int:32; + Int:32; + #endif +}; + +#ifdef MAGNUM_BUILD_DEPRECATED /** @brief @copybrief MeshVisualizerGL2D * @m_deprecated_since_latest Use @ref MeshVisualizerGL2D instead. */ @@ -60,10 +452,8 @@ typedef CORRADE_DEPRECATED("use MeshVisualizerGL3D instead") MeshVisualizerGL3D * @m_deprecated_since{2020,06} Use @ref MeshVisualizerGL3D instead. */ typedef CORRADE_DEPRECATED("use MeshVisualizerGL3D instead") MeshVisualizerGL3D MeshVisualizer; +#endif }} -#else -#error use Magnum/Shaders/MeshVisualizerGL.h and the MeshVisualizerGL2D / MeshVisualizer3D class instead -#endif #endif diff --git a/src/Magnum/Shaders/MeshVisualizer.vert b/src/Magnum/Shaders/MeshVisualizer.vert index a67ebc024..44b270b50 100644 --- a/src/Magnum/Shaders/MeshVisualizer.vert +++ b/src/Magnum/Shaders/MeshVisualizer.vert @@ -32,8 +32,13 @@ #define out varying #endif +#ifndef RUNTIME_CONST +#define const +#endif + /* Uniforms */ +#ifndef UNIFORM_BUFFERS #ifdef TWO_DIMENSIONS #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) @@ -97,6 +102,92 @@ uniform highp float lineLength ; #endif +/* Uniform buffers */ + +#else +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 0) +#endif +uniform highp uint drawOffset + #ifndef GL_ES + = 0u + #endif + ; + +#ifdef TWO_DIMENSIONS +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 1 + #endif +) uniform TransformationProjection { + highp mat3 transformationProjectionMatrices[DRAW_COUNT]; +}; +#elif defined(THREE_DIMENSIONS) +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 0 + #endif +) uniform Projection { + highp mat4 projectionMatrix; +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 1 + #endif +) uniform Transformation { + highp mat4 transformationMatrices[DRAW_COUNT]; +}; +#else +#error +#endif + +/* Keep in sync with MeshVisualizer.geom and MeshVisualizer.frag. Can't + "outsource" to a common file because the #extension directives need to be + always before any code. */ +struct DrawUniform { + #ifdef THREE_DIMENSIONS + highp mat3 normalMatrix; /* actually mat3x4 */ + #elif !defined(TWO_DIMENSIONS) + #error + #endif + highp uvec4 materialIdReservedReservedReservedReserved; + #define draw_materialIdReserved materialIdReservedReservedReservedReserved.x +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 2 + #endif +) uniform Draw { + DrawUniform draws[DRAW_COUNT]; +}; + +/* Keep in sync with MeshVisualizer.geom and MeshVisualizer.frag. Can't + "outsource" to a common file because the #extension directives need to be + always before any code. */ +struct MaterialUniform { + lowp vec4 color; + lowp vec4 wireframeColor; + lowp vec4 wireframeWidthColorMapOffsetColorMapScaleLineWidth; + #define material_wireframeWidth wireframeWidthColorMapOffsetColorMapScaleLineWidth.x + #define material_colorMapOffset wireframeWidthColorMapOffsetColorMapScaleLineWidth.y + #define material_colorMapScale wireframeWidthColorMapOffsetColorMapScaleLineWidth.z + #define material_lineWidth wireframeWidthColorMapOffsetColorMapScaleLineWidth.w + lowp vec4 lineLengthSmoothnessReservedReserved; + #define material_lineLength lineLengthSmoothnessReservedReserved.x + #define material_smoothness lineLengthSmoothnessReservedReserved.y +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 4 + #endif +) uniform Material { + MaterialUniform materials[MATERIAL_COUNT]; +}; +#endif + /* Inputs */ #ifdef EXPLICIT_ATTRIB_LOCATION @@ -189,6 +280,23 @@ out highp vec4 normalEndpoint; #endif void main() { + #ifdef UNIFORM_BUFFERS + #ifdef TWO_DIMENSIONS + highp const mat3 transformationProjectionMatrix = transformationProjectionMatrices[drawOffset]; + #elif defined(THREE_DIMENSIONS) + highp const mat4 transformationMatrix = transformationMatrices[drawOffset]; + #else + #error + #endif + #if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(BITANGENT_FROM_TANGENT_DIRECTION) || defined(NORMAL_DIRECTION) + mediump const mat3 normalMatrix = draws[drawOffset].normalMatrix; + #endif + mediump const uint materialId = draws[drawOffset].draw_materialIdReserved & 0xffffu; + lowp float colorMapOffset = materials[materialId].material_colorMapOffset; + lowp float colorMapScale = materials[materialId].material_colorMapScale; + highp float lineLength = materials[materialId].material_lineLength; + #endif + #ifdef TWO_DIMENSIONS gl_Position.xywz = vec4(transformationProjectionMatrix*vec3(position, 1.0), 0.0); #elif defined(THREE_DIMENSIONS) diff --git a/src/Magnum/Shaders/MeshVisualizerGL.cpp b/src/Magnum/Shaders/MeshVisualizerGL.cpp index a81eeb037..6d920a597 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.cpp +++ b/src/Magnum/Shaders/MeshVisualizerGL.cpp @@ -39,6 +39,10 @@ #include "Magnum/GL/Shader.h" #include "Magnum/GL/Texture.h" +#ifndef MAGNUM_TARGET_GLES2 +#include "Magnum/GL/Buffer.h" +#endif + #include "Magnum/Shaders/Implementation/CreateCompatibilityShader.h" namespace Magnum { namespace Shaders { @@ -48,11 +52,36 @@ namespace { /* First four taken by Phong (A/D/S/N) */ ColorMapTextureUnit = 4 }; + + #ifndef MAGNUM_TARGET_GLES2 + enum: Int { + ProjectionBufferBinding = 0, + /* Not using the zero binding to avoid conflicts with + ProjectionBufferBinding from the 3D variant which can likely stay + bound to the same buffer for the whole time */ + TransformationProjectionBufferBinding = 1, + TransformationBufferBinding = 1, + DrawBufferBinding = 2, + /* Binding 3 is commonly used by TextureTransformationBufferBinding, + leave it reserved */ + MaterialBufferBinding = 4, + }; + #endif } namespace Implementation { -MeshVisualizerGLBase::MeshVisualizerGLBase(FlagsBase flags): _flags{flags} { +MeshVisualizerGLBase::MeshVisualizerGLBase(FlagsBase flags + #ifndef MAGNUM_TARGET_GLES2 + , const UnsignedInt materialCount, const UnsignedInt drawCount + #endif +): + _flags{flags} + #ifndef MAGNUM_TARGET_GLES2 + , _materialCount{materialCount}, + _drawCount{drawCount} + #endif +{ #ifndef MAGNUM_TARGET_GLES2 #ifndef CORRADE_NO_ASSERT Int countMutuallyExclusive = 0; @@ -64,6 +93,11 @@ MeshVisualizerGLBase::MeshVisualizerGLBase(FlagsBase flags): _flags{flags} { "Shaders::MeshVisualizerGL: Flag::InstancedObjectId, Flag::VertexId and Flag::PrimitiveId are mutually exclusive", ); #endif + #ifndef MAGNUM_TARGET_GLES + if(flags >= FlagBase::UniformBuffers) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); + #endif + #ifndef MAGNUM_TARGET_GLES2 if(_flags & FlagBase::Wireframe && !(_flags & FlagBase::NoGeometryShader)) { #ifndef MAGNUM_TARGET_GLES @@ -126,6 +160,16 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra "#define SUBSCRIPTING_WORKAROUND\n" : "") #endif ; + #ifndef MAGNUM_TARGET_GLES2 + if(_flags >= FlagBase::UniformBuffers) { + vert.addSource(Utility::formatString( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n" + "#define MATERIAL_COUNT {}\n", + _drawCount, + _materialCount)); + } + #endif frag.addSource(_flags & FlagBase::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") #ifndef MAGNUM_TARGET_GLES2 .addSource(_flags & FlagBase::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") @@ -136,11 +180,25 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra "#define PRIMITIVE_ID\n") : "") #endif ; + #ifndef MAGNUM_TARGET_GLES2 + if(_flags >= FlagBase::UniformBuffers) { + frag.addSource(Utility::formatString( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n" + "#define MATERIAL_COUNT {}\n", + _drawCount, + _materialCount)); + } + #endif return version; } MeshVisualizerGLBase& MeshVisualizerGLBase::setColor(const Color4& color) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= FlagBase::UniformBuffers), + "Shaders::MeshVisualizerGL::setColor(): the shader was created with uniform buffers enabled", *this); + #endif #ifndef MAGNUM_TARGET_GLES2 CORRADE_ASSERT(_flags & (FlagBase::Wireframe|FlagBase::InstancedObjectId|FlagBase::VertexId|FlagBase::PrimitiveId), "Shaders::MeshVisualizerGL::setColor(): the shader was not created with wireframe or object/vertex/primitive ID enabled", *this); @@ -153,6 +211,10 @@ MeshVisualizerGLBase& MeshVisualizerGLBase::setColor(const Color4& color) { } MeshVisualizerGLBase& MeshVisualizerGLBase::setWireframeColor(const Color4& color) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= FlagBase::UniformBuffers), + "Shaders::MeshVisualizerGL::setWireframeColor(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(_flags & FlagBase::Wireframe, "Shaders::MeshVisualizerGL::setWireframeColor(): the shader was not created with wireframe enabled", *this); setUniform(_wireframeColorUniform, color); @@ -160,6 +222,10 @@ MeshVisualizerGLBase& MeshVisualizerGLBase::setWireframeColor(const Color4& colo } MeshVisualizerGLBase& MeshVisualizerGLBase::setWireframeWidth(const Float width) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= FlagBase::UniformBuffers), + "Shaders::MeshVisualizerGL::setWireframeWidth(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(_flags & FlagBase::Wireframe, "Shaders::MeshVisualizerGL::setWireframeWidth(): the shader was not created with wireframe enabled", *this); setUniform(_wireframeWidthUniform, width); @@ -168,12 +234,41 @@ MeshVisualizerGLBase& MeshVisualizerGLBase::setWireframeWidth(const Float width) #ifndef MAGNUM_TARGET_GLES2 MeshVisualizerGLBase& MeshVisualizerGLBase::setColorMapTransformation(const Float offset, const Float scale) { + CORRADE_ASSERT(!(_flags >= FlagBase::UniformBuffers), + "Shaders::MeshVisualizerGL::setColorMapTransformation(): the shader was created with uniform buffers enabled", *this); CORRADE_ASSERT(_flags & (FlagBase::InstancedObjectId|FlagBase::VertexId|FlagBase::PrimitiveId), "Shaders::MeshVisualizerGL::setColorMapTransformation(): the shader was not created with object/vertex/primitive ID enabled", *this); setUniform(_colorMapOffsetScaleUniform, Vector2{offset, scale}); return *this; } +#endif +#ifndef MAGNUM_TARGET_GLES2 +MeshVisualizerGLBase& MeshVisualizerGLBase::setDrawOffset(const UnsignedInt offset) { + CORRADE_ASSERT(_flags >= FlagBase::UniformBuffers, + "Shaders::MeshVisualizerGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(offset < _drawCount, + "Shaders::MeshVisualizerGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); + setUniform(_drawOffsetUniform, offset); + return *this; +} + +MeshVisualizerGLBase& MeshVisualizerGLBase::bindMaterialBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= FlagBase::UniformBuffers, + "Shaders::MeshVisualizerGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding); + return *this; +} + +MeshVisualizerGLBase& MeshVisualizerGLBase::bindMaterialBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= FlagBase::UniformBuffers, + "Shaders::MeshVisualizerGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); + return *this; +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 MeshVisualizerGLBase& MeshVisualizerGLBase::bindColorMapTexture(GL::Texture2D& texture) { CORRADE_ASSERT(_flags & (FlagBase::InstancedObjectId|FlagBase::VertexId|FlagBase::PrimitiveId), "Shaders::MeshVisualizerGL::bindColorMapTexture(): the shader was not created with object/vertex/primitive ID enabled", *this); @@ -184,7 +279,15 @@ MeshVisualizerGLBase& MeshVisualizerGLBase::bindColorMapTexture(GL::Texture2D& t } -MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags): Implementation::MeshVisualizerGLBase{Implementation::MeshVisualizerGLBase::FlagBase(UnsignedShort(flags))} { +MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags + #ifndef MAGNUM_TARGET_GLES2 + , const UnsignedInt materialCount, const UnsignedInt drawCount + #endif +): Implementation::MeshVisualizerGLBase{Implementation::MeshVisualizerGLBase::FlagBase(UnsignedShort(flags)) + #ifndef MAGNUM_TARGET_GLES2 + , materialCount, drawCount + #endif +} { #ifndef MAGNUM_TARGET_GLES2 CORRADE_ASSERT(flags & ((Flag::Wireframe|Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId) & ~Flag::NoGeometryShader), "Shaders::MeshVisualizerGL2D: at least one visualization feature has to be enabled", ); @@ -193,6 +296,16 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags): Implementation::MeshV "Shaders::MeshVisualizerGL2D: at least Flag::Wireframe has to be enabled", ); #endif + /* Has to be here and not in the base class in order to have it exit the + constructor when testing for asserts -- GLSL compilation would fail + otherwise */ + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || materialCount, + "Shaders::MeshVisualizerGL2D: material count can't be zero", ); + CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || drawCount, + "Shaders::MeshVisualizerGL2D: draw count can't be zero", ); + #endif + #ifndef MAGNUM_TARGET_GLES const GL::Context& context = GL::Context::current(); #endif @@ -215,8 +328,12 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags): Implementation::MeshV nothing actually needs it, as that makes checks much simpler in the shader code */ .addSource((flags & Flag::NoGeometryShader) || !(flags & Flag::Wireframe) ? - "#define NO_GEOMETRY_SHADER\n" : "") - .addSource(rs.get("generic.glsl")) + "#define NO_GEOMETRY_SHADER\n" : ""); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) + frag.addSource("#define TWO_DIMENSIONS\n"); + #endif + frag.addSource(rs.get("generic.glsl")) .addSource(rs.get("MeshVisualizer.frag")); #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) @@ -230,8 +347,19 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags): Implementation::MeshV .addSource(_flags & FlagBase::PrimitiveId ? (_flags >= FlagBase::PrimitiveIdFromVertexId ? "#define PRIMITIVE_ID_FROM_VERTEX_ID\n" : - "#define PRIMITIVE_ID\n") : "") - .addSource(rs.get("MeshVisualizer.geom")); + "#define PRIMITIVE_ID\n") : ""); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + geom->addSource(Utility::formatString( + "#define TWO_DIMENSIONS\n" + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n" + "#define MATERIAL_COUNT {}\n", + _drawCount, + _materialCount)); + } + #endif + geom->addSource(rs.get("MeshVisualizer.geom")); } #else static_cast(version); @@ -276,25 +404,35 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags): Implementation::MeshV if(!context.isExtensionSupported(version)) #endif { - _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); - if(flags & (Flag::Wireframe + /* This one is used also in the UBO case as it's usually a global + setting */ + if((flags & Flag::Wireframe) && !(flags & Flag::NoGeometryShader)) + _viewportSizeUniform = uniformLocation("viewportSize"); + + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + _drawOffsetUniform = uniformLocation("drawOffset"); + } else + #endif + { + _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); + if(flags & (Flag::Wireframe + #ifndef MAGNUM_TARGET_GLES2 + |Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId + #endif + )) + _colorUniform = uniformLocation("color"); + if(flags & Flag::Wireframe) { + _wireframeColorUniform = uniformLocation("wireframeColor"); + _wireframeWidthUniform = uniformLocation("wireframeWidth"); + _smoothnessUniform = uniformLocation("smoothness"); + } #ifndef MAGNUM_TARGET_GLES2 - |Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId + if(flags & (Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId)) { + _colorMapOffsetScaleUniform = uniformLocation("colorMapOffsetScale"); + } #endif - )) - _colorUniform = uniformLocation("color"); - if(flags & Flag::Wireframe) { - _wireframeColorUniform = uniformLocation("wireframeColor"); - _wireframeWidthUniform = uniformLocation("wireframeWidth"); - _smoothnessUniform = uniformLocation("smoothness"); - if(!(flags & Flag::NoGeometryShader)) - _viewportSizeUniform = uniformLocation("viewportSize"); - } - #ifndef MAGNUM_TARGET_GLES2 - if(flags & (Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId)) { - _colorMapOffsetScaleUniform = uniformLocation("colorMapOffsetScale"); } - #endif } #ifndef MAGNUM_TARGET_GLES2 @@ -305,31 +443,50 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags): Implementation::MeshV if(flags & (Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId)) { setUniform(uniformLocation("colorMapTexture"), ColorMapTextureUnit); } + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + setUniformBlockBinding(uniformBlockIndex("TransformationProjection"), TransformationProjectionBufferBinding); + setUniformBlockBinding(uniformBlockIndex("Draw"), DrawBufferBinding); + setUniformBlockBinding(uniformBlockIndex("Material"), MaterialBufferBinding); + } + #endif } #endif /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ #ifdef MAGNUM_TARGET_GLES - setTransformationProjectionMatrix(Matrix3{Math::IdentityInit}); - if(flags & (Flag::Wireframe + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + /* Viewport size is zero by default */ + /* Draw offset is zero by default */ + } else + #endif + { + setTransformationProjectionMatrix(Matrix3{Math::IdentityInit}); + if(flags & (Flag::Wireframe + #ifndef MAGNUM_TARGET_GLES2 + |Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId + #endif + )) + setColor(Color3(1.0f)); + if(flags & Flag::Wireframe) { + /* Viewport size is zero by default */ + setWireframeColor(Color3{0.0f}); + setWireframeWidth(1.0f); + setSmoothness(2.0f); + } #ifndef MAGNUM_TARGET_GLES2 - |Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId + if(flags & (Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId)) + setColorMapTransformation(1.0f/512.0f, 1.0f/256.0f); #endif - )) - setColor(Color3(1.0f)); - if(flags & Flag::Wireframe) { - /* Viewport size is zero by default */ - setWireframeColor(Color3{0.0f}); - setWireframeWidth(1.0f); - setSmoothness(2.0f); } - #ifndef MAGNUM_TARGET_GLES2 - if(flags & (Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId)) - setColorMapTransformation(1.0f/512.0f, 1.0f/256.0f); - #endif #endif } +#ifndef MAGNUM_TARGET_GLES2 +MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags): MeshVisualizerGL2D{flags, 1, 1} {} +#endif + MeshVisualizerGL2D& MeshVisualizerGL2D::setViewportSize(const Vector2& size) { /* Not asserting here, since the relation to wireframe is a bit vague. Also it's an ugly hack that should be removed, ideally. */ @@ -339,11 +496,19 @@ MeshVisualizerGL2D& MeshVisualizerGL2D::setViewportSize(const Vector2& size) { } MeshVisualizerGL2D& MeshVisualizerGL2D::setTransformationProjectionMatrix(const Matrix3& matrix) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags() >= Flag::UniformBuffers), + "Shaders::MeshVisualizerGL2D::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_transformationProjectionMatrixUniform, matrix); return *this; } MeshVisualizerGL2D& MeshVisualizerGL2D::setSmoothness(const Float smoothness) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags() >= Flag::UniformBuffers), + "Shaders::MeshVisualizerGL2D::setSmoothness(): the shader was created with uniform buffers enabled", *this); + #endif /* This is a bit vaguely related but less vague than setViewportSize() so asserting in this case. */ CORRADE_ASSERT(flags() & Flag::Wireframe, @@ -352,7 +517,45 @@ MeshVisualizerGL2D& MeshVisualizerGL2D::setSmoothness(const Float smoothness) { return *this; } -MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags): Implementation::MeshVisualizerGLBase{Implementation::MeshVisualizerGLBase::FlagBase(UnsignedShort(flags))} { +#ifndef MAGNUM_TARGET_GLES2 +MeshVisualizerGL2D& MeshVisualizerGL2D::bindTransformationProjectionBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(flags() >= Flag::UniformBuffers, + "Shaders::MeshVisualizerGL2D::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); + return *this; +} + +MeshVisualizerGL2D& MeshVisualizerGL2D::bindTransformationProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(flags() >= Flag::UniformBuffers, + "Shaders::MeshVisualizerGL2D::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); + return *this; +} + +MeshVisualizerGL2D& MeshVisualizerGL2D::bindDrawBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(flags() >= Flag::UniformBuffers, + "Shaders::MeshVisualizerGL2D::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding); + return *this; +} + +MeshVisualizerGL2D& MeshVisualizerGL2D::bindDrawBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(flags() >= Flag::UniformBuffers, + "Shaders::MeshVisualizerGL2D::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); + return *this; +} +#endif + +MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags + #ifndef MAGNUM_TARGET_GLES2 + , const UnsignedInt materialCount, const UnsignedInt drawCount + #endif +): Implementation::MeshVisualizerGLBase{Implementation::MeshVisualizerGLBase::FlagBase(UnsignedShort(flags)) + #ifndef MAGNUM_TARGET_GLES2 + , materialCount, drawCount + #endif +} { #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) CORRADE_ASSERT(flags & ((Flag::Wireframe|Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection|Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId) & ~Flag::NoGeometryShader), "Shaders::MeshVisualizerGL3D: at least one visualization feature has to be enabled", ); @@ -368,6 +571,16 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags): Implementation::MeshV "Shaders::MeshVisualizerGL3D: at least Flag::Wireframe has to be enabled", ); #endif + /* Has to be here and not in the base class in order to have it exit the + constructor when testing for asserts -- GLSL compilation would fail + otherwise */ + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || materialCount, + "Shaders::MeshVisualizerGL3D: material count can't be zero", ); + CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || drawCount, + "Shaders::MeshVisualizerGL3D: draw count can't be zero", ); + #endif + #ifndef MAGNUM_TARGET_GLES const GL::Context& context = GL::Context::current(); #endif @@ -399,7 +612,8 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags): Implementation::MeshV .addSource(flags & Flag::BitangentDirection ? "#define BITANGENT_DIRECTION\n" : "") .addSource(flags & Flag::NormalDirection ? "#define NORMAL_DIRECTION\n" : "") #endif - .addSource(rs.get("generic.glsl")) + ; + vert.addSource(rs.get("generic.glsl")) .addSource(rs.get("MeshVisualizer.vert")); frag /* Pass NO_GEOMETRY_SHADER not only when NoGeometryShader but also when @@ -413,7 +627,12 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags): Implementation::MeshV #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) .addSource(flags & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection) ? "#define TBN_DIRECTION\n" : "") #endif - .addSource(rs.get("generic.glsl")) + ; + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) + frag.addSource("#define THREE_DIMENSIONS\n"); + #endif + frag.addSource(rs.get("generic.glsl")) .addSource(rs.get("MeshVisualizer.frag")); #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) @@ -438,8 +657,19 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags): Implementation::MeshV "#define PRIMITIVE_ID\n") : "") .addSource(flags & Flag::TangentDirection ? "#define TANGENT_DIRECTION\n" : "") .addSource(flags & (Flag::BitangentDirection|Flag::BitangentFromTangentDirection) ? "#define BITANGENT_DIRECTION\n" : "") - .addSource(flags & Flag::NormalDirection ? "#define NORMAL_DIRECTION\n" : "") - .addSource(rs.get("MeshVisualizer.geom")); + .addSource(flags & Flag::NormalDirection ? "#define NORMAL_DIRECTION\n" : ""); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + geom->addSource(Utility::formatString( + "#define THREE_DIMENSIONS\n" + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n" + "#define MATERIAL_COUNT {}\n", + _drawCount, + _materialCount)); + } + #endif + geom->addSource(rs.get("MeshVisualizer.geom")); } #else static_cast(version); @@ -495,39 +725,53 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags): Implementation::MeshV if(!context.isExtensionSupported(version)) #endif { - _transformationMatrixUniform = uniformLocation("transformationMatrix"); - _projectionMatrixUniform = uniformLocation("projectionMatrix"); - if(flags & (Flag::Wireframe - #ifndef MAGNUM_TARGET_GLES2 - |Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId - #endif - )) - _colorUniform = uniformLocation("color"); - if(flags & Flag::Wireframe) { - _wireframeColorUniform = uniformLocation("wireframeColor"); - _wireframeWidthUniform = uniformLocation("wireframeWidth"); - } - if(flags & (Flag::Wireframe + /* This one is used also in the UBO case as it's usually a global + setting */ + if(((flags & Flag::Wireframe) && !(flags & Flag::NoGeometryShader)) #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - |Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection + || (flags & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection)) #endif - )) { - _smoothnessUniform = uniformLocation("smoothness"); - if(!(flags & Flag::NoGeometryShader)) - _viewportSizeUniform = uniformLocation("viewportSize"); - } + ) + _viewportSizeUniform = uniformLocation("viewportSize"); + #ifndef MAGNUM_TARGET_GLES2 - if(flags & (Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId)) { - _colorMapOffsetScaleUniform = uniformLocation("colorMapOffsetScale"); - } + if(flags >= Flag::UniformBuffers) { + _drawOffsetUniform = uniformLocation("drawOffset"); + } else #endif - #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - if(flags & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection)) { - _normalMatrixUniform = uniformLocation("normalMatrix"); - _lineWidthUniform = uniformLocation("lineWidth"); - _lineLengthUniform = uniformLocation("lineLength"); + { + _transformationMatrixUniform = uniformLocation("transformationMatrix"); + _projectionMatrixUniform = uniformLocation("projectionMatrix"); + if(flags & (Flag::Wireframe + #ifndef MAGNUM_TARGET_GLES2 + |Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId + #endif + )) + _colorUniform = uniformLocation("color"); + if(flags & Flag::Wireframe) { + _wireframeColorUniform = uniformLocation("wireframeColor"); + _wireframeWidthUniform = uniformLocation("wireframeWidth"); + } + if(flags & (Flag::Wireframe + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + |Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection + #endif + )) { + _smoothnessUniform = uniformLocation("smoothness"); + } + #ifndef MAGNUM_TARGET_GLES2 + if(flags & (Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId)) { + _colorMapOffsetScaleUniform = uniformLocation("colorMapOffsetScale"); + } + #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + if(flags & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection)) { + _normalMatrixUniform = uniformLocation("normalMatrix"); + _lineWidthUniform = uniformLocation("lineWidth"); + _lineLengthUniform = uniformLocation("lineLength"); + } + #endif } - #endif } #ifndef MAGNUM_TARGET_GLES2 @@ -538,57 +782,87 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags): Implementation::MeshV if(flags & (Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId)) { setUniform(uniformLocation("colorMapTexture"), ColorMapTextureUnit); } + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + setUniformBlockBinding(uniformBlockIndex("Projection"), ProjectionBufferBinding); + setUniformBlockBinding(uniformBlockIndex("Transformation"), TransformationBufferBinding); + setUniformBlockBinding(uniformBlockIndex("Draw"), DrawBufferBinding); + setUniformBlockBinding(uniformBlockIndex("Material"), MaterialBufferBinding); + } + #endif } #endif /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ #ifdef MAGNUM_TARGET_GLES - setTransformationMatrix(Matrix4{Math::IdentityInit}); - setProjectionMatrix(Matrix4{Math::IdentityInit}); - if(flags & (Flag::Wireframe + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + /* Viewport size is zero by default */ + /* Draw offset is zero by default */ + } else + #endif + { + setTransformationMatrix(Matrix4{Math::IdentityInit}); + setProjectionMatrix(Matrix4{Math::IdentityInit}); + if(flags & (Flag::Wireframe + #ifndef MAGNUM_TARGET_GLES2 + |Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId + #endif + )) + setColor(Color3(1.0f)); + if(flags & Flag::Wireframe) { + /* Viewport size is zero by default */ + setWireframeColor(Color3{0.0f}); + setWireframeWidth(1.0f); + } + if(flags & (Flag::Wireframe + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + |Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection + #endif + )) { + setSmoothness(2.0f); + } #ifndef MAGNUM_TARGET_GLES2 - |Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId + if(flags & (Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId)) + setColorMapTransformation(1.0f/512.0f, 1.0f/256.0f); #endif - )) - setColor(Color3(1.0f)); - if(flags & Flag::Wireframe) { - /* Viewport size is zero by default */ - setWireframeColor(Color3{0.0f}); - setWireframeWidth(1.0f); - } - if(flags & (Flag::Wireframe #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - |Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection + if(flags & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection)) { + setNormalMatrix(Matrix3x3{Math::IdentityInit}); + setLineWidth(1.0f); + setLineLength(1.0f); + } #endif - )) { - setSmoothness(2.0f); - } - #ifndef MAGNUM_TARGET_GLES2 - if(flags & (Flag::InstancedObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId)) - setColorMapTransformation(1.0f/512.0f, 1.0f/256.0f); - #endif - #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - if(flags & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection)) { - setNormalMatrix(Matrix3x3{Math::IdentityInit}); - setLineWidth(1.0f); - setLineLength(1.0f); } #endif - #endif } +#ifndef MAGNUM_TARGET_GLES2 +MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags): MeshVisualizerGL3D{flags, 1, 1} {} +#endif + MeshVisualizerGL3D& MeshVisualizerGL3D::setTransformationMatrix(const Matrix4& matrix) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags() >= Flag::UniformBuffers), + "Shaders::MeshVisualizerGL3D::setTransformationMatrix(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_transformationMatrixUniform, matrix); return *this; } MeshVisualizerGL3D& MeshVisualizerGL3D::setProjectionMatrix(const Matrix4& matrix) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags() >= Flag::UniformBuffers), + "Shaders::MeshVisualizerGL3D::setProjectionMatrix(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_projectionMatrixUniform, matrix); return *this; } #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) MeshVisualizerGL3D& MeshVisualizerGL3D::setNormalMatrix(const Matrix3x3& matrix) { + CORRADE_ASSERT(!(flags() >= Flag::UniformBuffers), + "Shaders::MeshVisualizerGL3D::setNormalMatrix(): the shader was created with uniform buffers enabled", *this); CORRADE_ASSERT(flags() & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection), "Shaders::MeshVisualizerGL3D::setNormalMatrix(): the shader was not created with TBN direction enabled", *this); setUniform(_normalMatrixUniform, matrix); @@ -610,6 +884,8 @@ MeshVisualizerGL3D& MeshVisualizerGL3D::setViewportSize(const Vector2& size) { #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) MeshVisualizerGL3D& MeshVisualizerGL3D::setLineWidth(const Float width) { + CORRADE_ASSERT(!(flags() >= Flag::UniformBuffers), + "Shaders::MeshVisualizerGL3D::setLineWidth(): the shader was created with uniform buffers enabled", *this); CORRADE_ASSERT(flags() & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection), "Shaders::MeshVisualizerGL3D::setLineWidth(): the shader was not created with TBN direction enabled", *this); setUniform(_lineWidthUniform, width); @@ -617,6 +893,8 @@ MeshVisualizerGL3D& MeshVisualizerGL3D::setLineWidth(const Float width) { } MeshVisualizerGL3D& MeshVisualizerGL3D::setLineLength(const Float length) { + CORRADE_ASSERT(!(flags() >= Flag::UniformBuffers), + "Shaders::MeshVisualizerGL3D::setLineLength(): the shader was created with uniform buffers enabled", *this); CORRADE_ASSERT(flags() & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection), "Shaders::MeshVisualizerGL3D::setLineLength(): the shader was not created with TBN direction enabled", *this); setUniform(_lineLengthUniform, length); @@ -625,6 +903,10 @@ MeshVisualizerGL3D& MeshVisualizerGL3D::setLineLength(const Float length) { #endif MeshVisualizerGL3D& MeshVisualizerGL3D::setSmoothness(const Float smoothness) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags() >= Flag::UniformBuffers), + "Shaders::MeshVisualizerGL3D::setSmoothness(): the shader was created with uniform buffers enabled", *this); + #endif #ifndef CORRADE_NO_ASSERT /* This is a bit vaguely related but less vague than setViewportSize() so asserting in this case. */ @@ -640,6 +922,50 @@ MeshVisualizerGL3D& MeshVisualizerGL3D::setSmoothness(const Float smoothness) { return *this; } +#ifndef MAGNUM_TARGET_GLES2 +MeshVisualizerGL3D& MeshVisualizerGL3D::bindProjectionBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(flags() >= Flag::UniformBuffers, + "Shaders::MeshVisualizerGL3D::bindProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, ProjectionBufferBinding); + return *this; +} + +MeshVisualizerGL3D& MeshVisualizerGL3D::bindProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(flags() >= Flag::UniformBuffers, + "Shaders::MeshVisualizerGL3D::bindProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, ProjectionBufferBinding, offset, size); + return *this; +} + +MeshVisualizerGL3D& MeshVisualizerGL3D::bindTransformationBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(flags() >= Flag::UniformBuffers, + "Shaders::MeshVisualizerGL3D::bindTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TransformationBufferBinding); + return *this; +} + +MeshVisualizerGL3D& MeshVisualizerGL3D::bindTransformationBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(flags() >= Flag::UniformBuffers, + "Shaders::MeshVisualizerGL3D::bindTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TransformationBufferBinding, offset, size); + return *this; +} + +MeshVisualizerGL3D& MeshVisualizerGL3D::bindDrawBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(flags() >= Flag::UniformBuffers, + "Shaders::MeshVisualizerGL3D::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding); + return *this; +} + +MeshVisualizerGL3D& MeshVisualizerGL3D::bindDrawBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(flags() >= Flag::UniformBuffers, + "Shaders::MeshVisualizerGL3D::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); + return *this; +} +#endif + Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flag value) { debug << "Shaders::MeshVisualizerGL2D::Flag" << Debug::nospace; @@ -656,6 +982,9 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flag value) { #endif _c(PrimitiveIdFromVertexId) #endif + #ifndef MAGNUM_TARGET_GLES2 + _c(UniformBuffers) + #endif #undef _c /* LCOV_EXCL_STOP */ } @@ -685,6 +1014,9 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL3D::Flag value) { #endif _c(PrimitiveIdFromVertexId) #endif + #ifndef MAGNUM_TARGET_GLES2 + _c(UniformBuffers) + #endif #undef _c /* LCOV_EXCL_STOP */ } @@ -703,7 +1035,10 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flags value) { MeshVisualizerGL2D::Flag::VertexId, MeshVisualizerGL2D::Flag::PrimitiveIdFromVertexId, /* Superset of PrimitiveId */ #ifndef MAGNUM_TARGET_WEBGL - MeshVisualizerGL2D::Flag::PrimitiveId + MeshVisualizerGL2D::Flag::PrimitiveId, + #endif + #ifndef MAGNUM_TARGET_GLES2 + MeshVisualizerGL2D::Flag::UniformBuffers #endif #endif }); @@ -726,7 +1061,10 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL3D::Flags value) { MeshVisualizerGL3D::Flag::VertexId, MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId, /* Superset of PrimitiveId */ #ifndef MAGNUM_TARGET_WEBGL - MeshVisualizerGL3D::Flag::PrimitiveId + MeshVisualizerGL3D::Flag::PrimitiveId, + #endif + #ifndef MAGNUM_TARGET_GLES2 + MeshVisualizerGL3D::Flag::UniformBuffers #endif #endif }); diff --git a/src/Magnum/Shaders/MeshVisualizerGL.h b/src/Magnum/Shaders/MeshVisualizerGL.h index 949954a05..ca21d8de9 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.h +++ b/src/Magnum/Shaders/MeshVisualizerGL.h @@ -53,14 +53,20 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgr InstancedObjectId = 1 << 2, VertexId = 1 << 3, PrimitiveId = 1 << 4, - PrimitiveIdFromVertexId = (1 << 5)|PrimitiveId + PrimitiveIdFromVertexId = (1 << 5)|PrimitiveId, + /* bit 6, 7, 8, 9 used by 3D-specific TBN visualization */ + UniformBuffers = 1 << 10 #endif }; typedef Containers::EnumSet FlagsBase; CORRADE_ENUMSET_FRIEND_OPERATORS(FlagsBase) - explicit MeshVisualizerGLBase(FlagsBase flags); + explicit MeshVisualizerGLBase(FlagsBase flags + #ifndef MAGNUM_TARGET_GLES2 + , UnsignedInt materialCount, UnsignedInt drawCount + #endif + ); explicit MeshVisualizerGLBase(NoCreateT) noexcept: GL::AbstractShaderProgram{NoCreate} {} MAGNUM_SHADERS_LOCAL GL::Version setupShaders(GL::Shader& vert, GL::Shader& frag, const Utility::Resource& rs) const; @@ -73,6 +79,12 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgr MeshVisualizerGLBase& bindColorMapTexture(GL::Texture2D& texture); #endif + #ifndef MAGNUM_TARGET_GLES2 + MeshVisualizerGLBase& setDrawOffset(UnsignedInt offset); + MeshVisualizerGLBase& bindMaterialBuffer(GL::Buffer& buffer); + MeshVisualizerGLBase& bindMaterialBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + #endif + /* Prevent accidentally calling irrelevant functions */ #ifndef MAGNUM_TARGET_GLES using GL::AbstractShaderProgram::drawTransformFeedback; @@ -82,6 +94,9 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgr #endif FlagsBase _flags; + #ifndef MAGNUM_TARGET_GLES2 + UnsignedInt _materialCount{}, _drawCount{}; + #endif Int _colorUniform{1}, _wireframeColorUniform{2}, _wireframeWidthUniform{3}, @@ -89,6 +104,9 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgr _viewportSizeUniform{5}; #ifndef MAGNUM_TARGET_GLES2 Int _colorMapOffsetScaleUniform{6}; + /* Used instead of all other uniforms when Flag::UniformBuffers is set, + so it can alias them */ + Int _drawOffsetUniform{0}; #endif }; @@ -200,11 +218,26 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua /** @copydoc MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId */ #ifndef MAGNUM_TARGET_WEBGL - PrimitiveIdFromVertexId = (1 << 5)|PrimitiveId + PrimitiveIdFromVertexId = (1 << 5)|PrimitiveId, #else - PrimitiveIdFromVertexId = (1 << 5)|(1 << 4) + PrimitiveIdFromVertexId = (1 << 5)|(1 << 4), #endif #endif + + #ifndef MAGNUM_TARGET_GLES2 + /** + * Use uniform buffers. Expects that uniform data are supplied via + * @ref bindTransformationProjectionBuffer(), @ref bindDrawBuffer() + * and @ref bindMaterialBuffer() instead of direct uniform setters. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES + * 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL + * 1.0. + * @m_since_latest + */ + UniformBuffers = 1 << 10 + #endif }; /** @brief Flags */ @@ -215,9 +248,50 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * @param flags Flags * * At least @ref Flag::Wireframe is expected to be enabled. + * + * While this function is meant mainly for the classic uniform + * scenario (without @ref Flag::UniformBuffers set), it's equivalent to + * @ref MeshVisualizerGL2D(Flags, UnsignedInt, UnsignedInt) with + * @p materialCount and @p drawCount set to @cpp 1 @ce. */ explicit MeshVisualizerGL2D(Flags flags); + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Construct for a multi-draw scenario + * @param flags Flags + * @param materialCount Size of a @ref MeshVisualizerMaterialUniform + * buffer bound with @ref bindMaterialBuffer() + * @param drawCount Size of a @ref TransformationProjectionUniform2D + * / @ref MeshVisualizerMaterialUniform buffer bound with + * @ref bindTransformationProjectionBuffer() and + * @ref bindDrawBuffer() + * + * At least @ref Flag::Wireframe is expected to be enabled. + * + * If @p flags contains @ref Flag::UniformBuffers, @p materialCount and + * @p drawCount describe the uniform buffer sizes as these are required + * to have a statically defined size. The draw offset is then set via + * @ref setDrawOffset() and the per-draw materials are specified via + * @ref MeshVisualizerDrawUniform2D::materialId. + * + * If @p flags don't contain @ref Flag::UniformBuffers, + * @p materialCount and @p drawCount is ignored and the constructor + * behaves the same as @ref MeshVisualizerGL2D(Flags). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + /** @todo this constructor will eventually need to have also joint + count, per-vertex weight count, view count for multiview and clip + plane count ... and putting them in arbitrary order next to each + other is too error-prone, so it needs some other solution + (accepting pairs of parameter type and value like in GL context + creation, e.g., which will probably need a new enum as reusing Flag + for this might be too confusing) */ + explicit MeshVisualizerGL2D(Flags flags, UnsignedInt materialCount, UnsignedInt drawCount); + #endif + /** * @brief Construct without creating the underlying OpenGL object * @@ -249,8 +323,37 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua return Flag(UnsignedShort(Implementation::MeshVisualizerGLBase::_flags)); } + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Material count + * @m_since_latest + * + * Statically defined size of the @ref MeshVisualizerMaterialUniform + * uniform buffer. Has use only if @ref Flag::UniformBuffers is set. + * @see @ref bindMaterialBuffer() + * @requires_gles30 Not defined on OpenGL ES 2.0 builds. + * @requires_webgl20 Not defined on WebGL 1.0 builds. + */ + UnsignedInt materialCount() const { return _materialCount; } + + /** + * @brief Draw count + * @m_since_latest + * + * Statically defined size of each of the + * @ref TransformationProjectionUniform2D and + * @ref MeshVisualizerDrawUniform2D uniform buffers. Has use only if + * @ref Flag::UniformBuffers is set. + * @requires_gles30 Not defined on OpenGL ES 2.0 builds. + * @requires_webgl20 Not defined on WebGL 1.0 builds. + */ + UnsignedInt drawCount() const { return _drawCount; } + #endif + /** @{ * @name Uniform setters + * + * Used only if @ref Flag::UniformBuffers is not set. */ /** @@ -258,6 +361,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * @return Reference to self (for method chaining) * * Initial value is an identity matrix. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref TransformationProjectionUniform2D::transformationProjectionMatrix + * and call @ref bindTransformationProjectionBuffer() instead. */ MeshVisualizerGL2D& setTransformationProjectionMatrix(const Matrix3& matrix); @@ -280,6 +387,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * @ref Flag::PrimitiveId / @ref Flag::PrimitiveIdFromVertexId is * enabled. In case of the latter, the color is multiplied with the * color map coming from @ref bindColorMapTexture(). + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref MeshVisualizerMaterialUniform::color and call + * @ref bindMaterialBuffer() instead. */ MeshVisualizerGL2D& setColor(const Color4& color) { return static_cast(Implementation::MeshVisualizerGLBase::setColor(color)); @@ -291,6 +402,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * * Initial value is @cpp 0x000000ff_rgbaf @ce. Expects that * @ref Flag::Wireframe is enabled. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref MeshVisualizerMaterialUniform::wireframeColor and call + * @ref bindMaterialBuffer() instead. */ MeshVisualizerGL2D& setWireframeColor(const Color4& color) { return static_cast(Implementation::MeshVisualizerGLBase::setWireframeColor(color)); @@ -303,6 +418,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * The value is in screen space (depending on @ref setViewportSize()), * initial value is @cpp 1.0f @ce. Expects that @ref Flag::Wireframe is * enabled. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref MeshVisualizerMaterialUniform::wireframeWidth and call + * @ref bindMaterialBuffer() instead. */ MeshVisualizerGL2D& setWireframeWidth(Float width) { return static_cast(Implementation::MeshVisualizerGLBase::setWireframeWidth(width)); @@ -322,6 +441,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * Value is in screen space (depending on @ref setViewportSize()), * initial value is @cpp 2.0f @ce. Expects that @ref Flag::Wireframe is * enabled. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref MeshVisualizerMaterialUniform::smoothness and call + * @ref bindMaterialBuffer() instead. */ MeshVisualizerGL2D& setSmoothness(Float smoothness); @@ -329,6 +452,103 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * @} */ + #ifndef MAGNUM_TARGET_GLES2 + /** @{ + * @name Uniform buffer binding and related uniform setters + * + * Used if @ref Flag::UniformBuffers is set. + */ + + /** + * @brief Set a draw offset + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Specifies which item in the @ref TransformationProjectionUniform2D + * and @ref MeshVisualizerDrawUniform2D buffers bound with + * @ref bindTransformationProjectionBuffer() and @ref bindDrawBuffer() + * should be used for current draw. Expects that + * @ref Flag::UniformBuffers is set and @p offset is less than + * @ref drawCount(). Initial value is @cpp 0 @ce. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + MeshVisualizerGL2D& setDrawOffset(UnsignedInt offset) { + return static_cast(Implementation::MeshVisualizerGLBase::setDrawOffset(offset)); + } + + /** + * @brief Set a transformation and projection uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref drawCount() instances of + * @ref TransformationUniform3D. At the very least you need to call + * also @ref bindDrawBuffer() and @ref bindMaterialBuffer(). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + MeshVisualizerGL2D& bindTransformationProjectionBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + MeshVisualizerGL2D& bindTransformationProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a draw uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref drawCount() instances of + * @ref MeshVisualizerDrawUniform2D. At the very least you need to call + * also @ref bindTransformationProjectionBuffer() and + * @ref bindMaterialBuffer(). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + MeshVisualizerGL2D& bindDrawBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + MeshVisualizerGL2D& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a material uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref materialCount() instances of + * @ref MeshVisualizerMaterialUniform. At the very least you need to + * call also @ref bindTransformationProjectionBuffer() and + * @ref bindDrawBuffer(). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + MeshVisualizerGL2D& bindMaterialBuffer(GL::Buffer& buffer) { + return static_cast(Implementation::MeshVisualizerGLBase::bindMaterialBuffer(buffer)); + } + /** + * @overload + * @m_since_latest + */ + MeshVisualizerGL2D& bindMaterialBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size) { + return static_cast(Implementation::MeshVisualizerGLBase::bindMaterialBuffer(buffer, offset, size)); + } + + /** + * @} + */ + #endif + /** @{ * @name Texture binding */ @@ -761,7 +981,23 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * @requires_gles Geometry shaders are not available in WebGL. * @m_since{2020,06} */ - NormalDirection = 1 << 9 + NormalDirection = 1 << 9, + #endif + + #ifndef MAGNUM_TARGET_GLES2 + /** + * Use uniform buffers. Expects that uniform data are supplied via + * @ref bindProjectionBuffer(), @ref bindTransformationBuffer(), + * @ref bindDrawBuffer() and @ref bindMaterialBuffer() instead of + * direct uniform setters. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES + * 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL + * 1.0. + * @m_since_latest + */ + UniformBuffers = 1 << 10 #endif }; @@ -776,6 +1012,11 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * @ref Flag::BitangentFromTangentDirection, * @ref Flag::BitangentDirection, @ref Flag::NormalDirection is * expected to be enabled. + * + * While this function is meant mainly for the classic uniform + * scenario (without @ref Flag::UniformBuffers set), it's equivalent to + * @ref MeshVisualizerGL3D(Flags, UnsignedInt, UnsignedInt) with + * @p materialCount and @p drawCount set to @cpp 1 @ce. */ explicit MeshVisualizerGL3D(Flags flags); @@ -788,6 +1029,46 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua explicit CORRADE_DEPRECATED("use MeshVisualizerGL3D(Flags) instead") MeshVisualizerGL3D(): MeshVisualizerGL3D{{}} {} #endif + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Construct for a multi-draw scenario + * @param flags Flags + * @param materialCount Size of a @ref MeshVisualizerMaterialUniform + * buffer bound with @ref bindMaterialBuffer() + * @param drawCount Size of a @ref ProjectionUniform3D / + * @ref TransformationUniform3D / + * @ref MeshVisualizerMaterialUniform buffer bound with + * @ref bindProjectionBuffer(), @ref bindTransformationBuffer() + * and @ref bindDrawBuffer() + * + * At least @ref Flag::Wireframe or one of @ref Flag::TangentDirection, + * @ref Flag::BitangentFromTangentDirection, + * @ref Flag::BitangentDirection, @ref Flag::NormalDirection is + * expected to be enabled. + * + * If @p flags contains @ref Flag::UniformBuffers, @p materialCount and + * @p drawCount describe the uniform buffer sizes as these are required + * to have a statically defined size. The draw offset is then set via + * @ref setDrawOffset() and the per-draw materials are specified via + * @ref MeshVisualizerDrawUniform3D::materialId. + * + * If @p flags don't contain @ref Flag::UniformBuffers, + * @p materialCount and @p drawCount is ignored and the constructor + * behaves the same as @ref MeshVisualizerGL3D(Flags). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + /** @todo this constructor will eventually need to have also joint + count, per-vertex weight count, view count for multiview and clip + plane count ... and putting them in arbitrary order next to each + other is too error-prone, so it needs some other solution + (accepting pairs of parameter type and value like in GL context + creation, e.g., which will probably need a new enum as reusing Flag + for this might be too confusing) */ + explicit MeshVisualizerGL3D(Flags flags, UnsignedInt materialCount, UnsignedInt drawCount); + #endif + /** * @brief Construct without creating the underlying OpenGL object * @@ -819,8 +1100,37 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua return Flag(UnsignedShort(Implementation::MeshVisualizerGLBase::_flags)); } + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Material count + * @m_since_latest + * + * Statically defined size of the @ref MeshVisualizerMaterialUniform + * uniform buffer. Has use only if @ref Flag::UniformBuffers is set. + * @see @ref bindMaterialBuffer() + * @requires_gles30 Not defined on OpenGL ES 2.0 builds. + * @requires_webgl20 Not defined on WebGL 1.0 builds. + */ + UnsignedInt materialCount() const { return _materialCount; } + + /** + * @brief Draw count + * @m_since_latest + * + * Statically defined size of each of the + * @ref TransformationProjectionUniform3D and + * @ref MeshVisualizerDrawUniform3D uniform buffers. Has use only if + * @ref Flag::UniformBuffers is set. + * @requires_gles30 Not defined on OpenGL ES 2.0 builds. + * @requires_webgl20 Not defined on WebGL 1.0 builds. + */ + UnsignedInt drawCount() const { return _drawCount; } + #endif + /** @{ * @name Uniform setters + * + * Used only if @ref Flag::UniformBuffers is not set. */ #ifdef MAGNUM_BUILD_DEPRECATED @@ -841,6 +1151,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * @return Reference to self (for method chaining) * * Initial value is an identity matrix. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref TransformationUniform3D::transformationMatrix and call + * @ref bindTransformationBuffer() instead. */ MeshVisualizerGL3D& setTransformationMatrix(const Matrix4& matrix); @@ -851,6 +1165,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * Initial value is an identity matrix. (i.e., an orthographic * projection of the default @f$ [ -\boldsymbol{1} ; \boldsymbol{1} ] @f$ * cube). + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref ProjectionUniform3D::projectionMatrix and call + * @ref bindProjectionBuffer() instead. */ MeshVisualizerGL3D& setProjectionMatrix(const Matrix4& matrix); @@ -865,6 +1183,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * enabled. The matrix doesn't need to be normalized, as * renormalization is done per-fragment anyway. * Initial value is an identity matrix. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref MeshVisualizerDrawUniform3D::normalMatrix and call + * @ref bindDrawBuffer() instead. * @requires_gl32 Extension @gl_extension{ARB,geometry_shader4} * @requires_gles30 Not defined in OpenGL ES 2.0. * @requires_gles32 Extension @gl_extension{ANDROID,extension_pack_es31a} / @@ -894,6 +1216,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * @ref Flag::PrimitiveId / @ref Flag::PrimitiveIdFromVertexId is * enabled. In case of the latter, the color is multiplied with the * color map coming from @ref bindColorMapTexture(). + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref MeshVisualizerMaterialUniform::color and call + * @ref bindMaterialBuffer() instead. */ MeshVisualizerGL3D& setColor(const Color4& color) { return static_cast(Implementation::MeshVisualizerGLBase::setColor(color)); @@ -905,6 +1231,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * * Initial value is @cpp 0x000000ff_rgbaf @ce. Expects that * @ref Flag::Wireframe is enabled. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref MeshVisualizerMaterialUniform::wireframeColor and call + * @ref bindMaterialBuffer() instead. */ MeshVisualizerGL3D& setWireframeColor(const Color4& color) { return static_cast(Implementation::MeshVisualizerGLBase::setWireframeColor(color)); @@ -917,6 +1247,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * The value is in screen space (depending on @ref setViewportSize()), * initial value is @cpp 1.0f @ce. Expects that @ref Flag::Wireframe is * enabled. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref MeshVisualizerMaterialUniform::wireframeWidth and call + * @ref bindMaterialBuffer() instead. */ MeshVisualizerGL3D& setWireframeWidth(Float width) { return static_cast(Implementation::MeshVisualizerGLBase::setWireframeWidth(width)); @@ -943,6 +1277,11 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * @ref FlatGL::setObjectId() "setObjectId()" uniform that's used to * offset the per-vertex / per-instance ID. Instead, you need to encode * the base offset into the @p offset parameter. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref MeshVisualizerMaterialUniform::colorMapOffset and + * @ref MeshVisualizerMaterialUniform::colorMapScale and call + * @ref bindMaterialBuffer() instead. * @requires_gles30 Object ID visualization requires integer attributes * while primitive ID visualization requires the `gl_VertexID` / * `gl_PrimitiveID` builtins, neither of which is available in @@ -969,6 +1308,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * @ref Flag::BitangentFromTangentDirection, * @ref Flag::BitangentDirection or @ref Flag::NormalDirection is * enabled. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref MeshVisualizerMaterialUniform::lineWidth and call + * @ref bindMaterialBuffer() instead. * @requires_gl32 Extension @gl_extension{ARB,geometry_shader4} * @requires_gles30 Not defined in OpenGL ES 2.0. * @requires_gles32 Extension @gl_extension{ANDROID,extension_pack_es31a} / @@ -987,6 +1330,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * @ref Flag::BitangentFromTangentDirection, * @ref Flag::BitangentDirection or @ref Flag::NormalDirection is * enabled. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref MeshVisualizerMaterialUniform::lineLength and call + * @ref bindMaterialBuffer() instead. * @requires_gl32 Extension @gl_extension{ARB,geometry_shader4} * @requires_gles30 Not defined in OpenGL ES 2.0. * @requires_gles32 Extension @gl_extension{ANDROID,extension_pack_es31a} / @@ -1006,6 +1353,10 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * @ref Flag::BitangentFromTangentDirection, * @ref Flag::BitangentDirection or @ref Flag::NormalDirection is * enabled. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref MeshVisualizerMaterialUniform::smoothness and call + * @ref bindMaterialBuffer() instead. */ MeshVisualizerGL3D& setSmoothness(Float smoothness); @@ -1013,6 +1364,124 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * @} */ + #ifndef MAGNUM_TARGET_GLES2 + /** @{ + * @name Uniform buffer binding and related uniform setters + * + * Used if @ref Flag::UniformBuffers is set. + */ + + /** + * @brief Set a draw offset + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Specifies which item in the @ref TransformationUniform3D and + * @ref MeshVisualizerDrawUniform3D buffers bound with + * @ref bindTransformationBuffer() and @ref bindDrawBuffer() should be + * used for current draw. Expects that @ref Flag::UniformBuffers is set + * and @p offset is less than @ref drawCount(). Initial value is + * @cpp 0 @ce. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + MeshVisualizerGL3D& setDrawOffset(UnsignedInt offset) { + return static_cast(Implementation::MeshVisualizerGLBase::setDrawOffset(offset)); + } + + /** + * @brief Set a projection uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain at least one instance of + * @ref ProjectionUniform3D. At the very least you need to call also + * @ref bindTransformationBuffer(), @ref bindDrawBuffer() and + * @ref bindMaterialBuffer(). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + MeshVisualizerGL3D& bindProjectionBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + MeshVisualizerGL3D& bindProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a transformation uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref drawCount() instances of + * @ref TransformationUniform3D. At the very least you need to call + * also @ref bindDrawBuffer() and @ref bindMaterialBuffer(). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + MeshVisualizerGL3D& bindTransformationBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + MeshVisualizerGL3D& bindTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a draw uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref drawCount() instances of + * @ref MeshVisualizerDrawUniform3D. At the very least you need to call + * also @ref bindProjectionBuffer(), @ref bindTransformationBuffer() + * and @ref bindMaterialBuffer(). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + MeshVisualizerGL3D& bindDrawBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + MeshVisualizerGL3D& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a material uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref materialCount() instances of + * @ref MeshVisualizerMaterialUniform. At the very least you need to + * call also @ref bindProjectionBuffer(), + * @ref bindTransformationBuffer() and @ref bindDrawBuffer(). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + MeshVisualizerGL3D& bindMaterialBuffer(GL::Buffer& buffer) { + return static_cast(Implementation::MeshVisualizerGLBase::bindMaterialBuffer(buffer)); + } + /** + * @overload + * @m_since_latest + */ + MeshVisualizerGL3D& bindMaterialBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size) { + return static_cast(Implementation::MeshVisualizerGLBase::bindMaterialBuffer(buffer, offset, size)); + } + + /** + * @} + */ + #endif + /** @{ * @name Texture binding */ diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index ca6fefe01..07c11a2fe 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -39,6 +39,7 @@ /* Uniforms */ +#ifndef UNIFORM_BUFFERS #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 4) #endif @@ -53,7 +54,6 @@ uniform lowp vec4 ambientColor ; #if LIGHT_COUNT - #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 5) #endif @@ -145,6 +145,78 @@ uniform lowp float lightRanges[LIGHT_COUNT] ; #endif +/* Uniform buffers */ + +#else +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 0) +#endif +uniform highp uint drawOffset + #ifndef GL_ES + = 0u + #endif + ; + +/* Keep in sync with Phong.vert. Can't "outsource" to a common file because + the #extension directive needs to be always before any code. */ +struct DrawUniform { + mediump mat3 normalMatrix; /* actually mat3x4 */ + highp uvec4 materialIdReservedObjectIdLightOffsetLightCount; + #define draw_materialIdReserved materialIdReservedObjectIdLightOffsetLightCount.x + #define draw_objectId materialIdReservedObjectIdLightOffsetLightCount.y + #define draw_lightOffset materialIdReservedObjectIdLightOffsetLightCount.z + #define draw_lightCount materialIdReservedObjectIdLightOffsetLightCount.w +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 2 + #endif +) uniform Draw { + DrawUniform draws[DRAW_COUNT]; +}; + +struct MaterialUniform { + lowp vec4 ambientColor; + lowp vec4 diffuseColor; + lowp vec4 specularColor; + mediump vec4 normalTextureScaleShininessAlphaMaskReserved; + #define material_normalTextureScale normalTextureScaleShininessAlphaMaskReserved.x + #define material_shininess normalTextureScaleShininessAlphaMaskReserved.y + #define material_alphaMask normalTextureScaleShininessAlphaMaskReserved.z +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 4 + #endif +) uniform Material { + MaterialUniform materials[MATERIAL_COUNT]; +}; + +/* Keep in sync with Phong.vert. Can't "outsource" to a common file because + the #extension directive needs to be always before any code. */ +struct LightUniform { + highp vec4 position; + lowp vec3 colorReserved; + #define light_color colorReserved.xyz + lowp vec4 specularColorReserved; + #define light_specularColor specularColorReserved.xyz + lowp vec4 rangeReservedReservedReserved; + #define light_range rangeReservedReservedReserved.x +}; + +#if LIGHT_COUNT +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 5 + #endif +) uniform Light { + LightUniform lights[LIGHT_COUNT]; +}; +#endif +#endif + /* Textures */ #ifdef AMBIENT_TEXTURE @@ -222,6 +294,28 @@ out highp uint fragmentObjectId; #endif void main() { + #ifdef UNIFORM_BUFFERS + #ifdef OBJECT_ID + highp const uint objectId = draws[drawOffset].draw_objectId; + #endif + mediump const uint materialId = draws[drawOffset].draw_materialIdReserved & 0xffffu; + lowp const vec4 ambientColor = materials[materialId].ambientColor; + #if LIGHT_COUNT + lowp const vec4 diffuseColor = materials[materialId].diffuseColor; + lowp const vec4 specularColor = materials[materialId].specularColor; + mediump const float shininess = materials[materialId].material_shininess; + #endif + #ifdef NORMAL_TEXTURE + mediump float normalTextureScale = materials[materialId].material_normalTextureScale; + #endif + #ifdef ALPHA_MASK + lowp const float alphaMask = materials[materialId].material_alphaMask; + #endif + #if LIGHT_COUNT + mediump const uint lightOffset = draws[drawOffset].draw_lightOffset; + #endif + #endif + lowp const vec4 finalAmbientColor = #ifdef AMBIENT_TEXTURE texture(ambientTexture, interpolatedTextureCoordinates)* @@ -273,7 +367,34 @@ void main() { #endif /* Add diffuse color for each light */ - for(int i = 0; i < LIGHT_COUNT; ++i) { + #ifndef UNIFORM_BUFFERS + for(int i = 0; i < LIGHT_COUNT; ++i) + #else + for(uint i = 0u, actualLightCount = min(uint(LIGHT_COUNT), draws[drawOffset].draw_lightCount); i < actualLightCount; ++i) + #endif + { + lowp const vec3 lightColor = + #ifndef UNIFORM_BUFFERS + lightColors[i] + #else + lights[lightOffset + i].light_color + #endif + ; + lowp const vec3 lightSpecularColor = + #ifndef UNIFORM_BUFFERS + lightSpecularColors[i] + #else + lights[lightOffset + i].light_specularColor + #endif + ; + lowp const float lightRange = + #ifndef UNIFORM_BUFFERS + lightRanges[i] + #else + lights[lightOffset + i].light_range + #endif + ; + /* Attenuation. Directional lights have the .w component set to 0, use that to make the distance zero -- which will then ensure the attenuation is always 1.0 */ @@ -281,19 +402,25 @@ void main() { /* If range is 0 for whatever reason, clamp it to a small value to avoid a NaN when dist is 0 as well (which is the case for directional lights). */ - highp float attenuation = clamp(1.0 - pow(dist/max(lightRanges[i], 0.0001), 4.0), 0.0, 1.0); + highp float attenuation = clamp(1.0 - pow(dist/max(lightRange, 0.0001), 4.0), 0.0, 1.0); attenuation = attenuation*attenuation/(1.0 + dist*dist); highp vec3 normalizedLightDirection = normalize(lightDirections[i].xyz); lowp float intensity = max(0.0, dot(normalizedTransformedNormal, normalizedLightDirection))*attenuation; - fragmentColor += vec4(finalDiffuseColor.rgb*lightColors[i]*intensity, finalDiffuseColor.a/float(LIGHT_COUNT)); + fragmentColor += vec4(finalDiffuseColor.rgb*lightColor*intensity, finalDiffuseColor.a/float( + #ifndef UNIFORM_BUFFERS + LIGHT_COUNT + #else + actualLightCount + #endif + )); /* Add specular color, if needed */ if(intensity > 0.001) { highp vec3 reflection = reflect(-normalizedLightDirection, normalizedTransformedNormal); /* Use attenuation for the specularity as well */ mediump float specularity = clamp(pow(max(0.0, dot(normalize(cameraDirection), reflection)), shininess), 0.0, 1.0)*attenuation; - fragmentColor += vec4(finalSpecularColor.rgb*lightSpecularColors[i].rgb*specularity, finalSpecularColor.a); + fragmentColor += vec4(finalSpecularColor.rgb*lightSpecularColor.rgb*specularity, finalSpecularColor.a); } } #endif diff --git a/src/Magnum/Shaders/Phong.h b/src/Magnum/Shaders/Phong.h index 24a5cd9af..8252f62e9 100644 --- a/src/Magnum/Shaders/Phong.h +++ b/src/Magnum/Shaders/Phong.h @@ -25,33 +25,460 @@ DEALINGS IN THE SOFTWARE. */ -#ifdef MAGNUM_BUILD_DEPRECATED /** @file - * @brief Typedef @ref Magnum::Shaders::Phong - * @m_deprecated_since_latest Use @ref Magnum/Shaders/PhongGL.h and the - * @ref Magnum::Shaders::PhongGL "PhongGL" class instead + * @brief Struct @ref Magnum::Shaders::PhongDrawUniform, @ref Magnum::Shaders::PhongMaterialUniform, @ref Magnum::Shaders::PhongLightUniform */ -#endif -#include "Magnum/configure.h" +#include "Magnum/Magnum.h" +#include "Magnum/Tags.h" +#include "Magnum/Math/Color.h" +#include "Magnum/Math/Matrix.h" #ifdef MAGNUM_BUILD_DEPRECATED #include #include "Magnum/Shaders/PhongGL.h" - -CORRADE_DEPRECATED_FILE("use Magnum/Shaders/PhongGL.h and the PhongGL class instead") +#endif namespace Magnum { namespace Shaders { +/** +@brief Per-draw uniform for Phong shaders +@m_since_latest + +Together with the generic @ref TransformationUniform3D contains parameters that +are specific to each draw call. Texture transformation, if needed, is supplied +separately in a @ref TextureTransformationUniform; material-related properties +are expected to be shared among multiple draw calls and thus are provided in a +separate @ref PhongMaterialUniform structure, referenced by @ref materialId. +@see @ref PhongGL::bindDrawBuffer() +*/ +struct PhongDrawUniform { + /** @brief Construct with default parameters */ + constexpr explicit PhongDrawUniform(DefaultInitT = DefaultInit) noexcept: normalMatrix{Math::IdentityInit}, materialId{0}, objectId{0}, lightOffset{0}, lightCount{0xffffffffu} {} + + /** @brief Construct without initializing the contents */ + explicit PhongDrawUniform(NoInitT) noexcept: normalMatrix{NoInit} {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref normalMatrix field + * @return Reference to self (for method chaining) + * + * The matrix is expanded to @relativeref{Magnum,Matrix3x4}, with the + * bottom row being zeros. + */ + PhongDrawUniform& setNormalMatrix(const Matrix3x3& matrix) { + normalMatrix = Matrix3x4{matrix}; + return *this; + } + + /** + * @brief Set the @ref materialId field + * @return Reference to self (for method chaining) + */ + PhongDrawUniform& setMaterialId(UnsignedInt id) { + materialId = id; + return *this; + } + + /** + * @brief Set the @ref objectId field + * @return Reference to self (for method chaining) + */ + PhongDrawUniform& setObjectId(UnsignedInt id) { + objectId = id; + return *this; + } + + /** + * @brief Set the @ref lightOffset and @ref lightCount fields + * @return Reference to self (for method chaining) + */ + PhongDrawUniform& setLightOffsetCount(UnsignedInt offset, UnsignedInt count) { + lightOffset = offset; + lightCount = count; + return *this; + } + + /** + * @} + */ + + /** + * @brief Normal matrix + * + * Default value is an identity matrix. The bottom row is unused and acts + * only as a padding to match uniform buffer packing rules. + * + * If @ref PhongGL::Flag::InstancedTransformation is enabled, the + * per-instance normal matrix coming from the @ref PhongGL::NormalMatrix + * attribute is applied first, before this one. + * @see @ref PhongGL::setNormalMatrix() + */ + Matrix3x4 normalMatrix; + + /** @var materialId + * @brief Material ID + * + * References a particular material from a @ref PhongMaterialUniform array. + * Useful when a UBO with more than one material is supplied or in a + * multi-draw scenario. Should be less than the material count passed to + * the @ref PhongGL::PhongGL(Flags, UnsignedInt, UnsignedInt, UnsignedInt) + * constructor. Default value is @cpp 0 @ce, meaning the first material + * gets used. + */ + + /* This field is an UnsignedInt in the shader and materialId is extracted + as (value & 0xffff), so the order has to be different on BE */ + #ifndef CORRADE_TARGET_BIG_ENDIAN + UnsignedShort materialId; + /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + UnsignedShort:16; /* reserved for skinOffset */ + #endif + #else + UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort materialId; + #endif + + /** + * @brief Object ID + * + * Unlike @ref materialId, this index is used only for the object ID + * framebuffer output, not to access any other uniform data. Default value + * is @cpp 0 @ce. + * + * Used only if @ref PhongGL::Flag::ObjectId is enabled, ignored otherwise. + * If @ref PhongGL::Flag::InstancedObjectId is enabled as well, this value + * is added to the ID coming from the @ref PhongGL::ObjectId attribute. + * @see @ref PhongGL::setObjectId() + */ + UnsignedInt objectId; + + /** + * @brief Light offset + * + * References the first light in the @ref PhongLightUniform array. Should + * be less than the light count passed to @ref PhongGL constructor. Default + * value is @cpp 0 @ce. + */ + UnsignedInt lightOffset; + + /** + * @brief Light count + * + * Specifies how many lights after the @p lightOffset are used from the + * @ref PhongLightUniform array. Gets clamped by the shader so it's + * together with @ref lightOffset not larger than the light count passed to + * @ref PhongGL constructor. Default value is @cpp 0xffffffffu @ce. + */ + UnsignedInt lightCount; +}; + +/** +@brief Material uniform for Phong shaders +@m_since_latest + +Describes material properties referenced from +@ref PhongDrawUniform::materialId. +@see @ref PhongGL::bindMaterialBuffer() +*/ +struct PhongMaterialUniform { + /** @brief Construct with default parameters */ + constexpr explicit PhongMaterialUniform(DefaultInitT = DefaultInit) noexcept: ambientColor{0.0f, 0.0f, 0.0f, 0.0f}, diffuseColor{1.0f, 1.0f, 1.0f, 1.0f}, specularColor{1.0f, 1.0f, 1.0f, 0.0f}, normalTextureScale{1.0f}, shininess{80.0f}, alphaMask{0.5f} {} + + /** @brief Construct without initializing the contents */ + explicit PhongMaterialUniform(NoInitT) noexcept: ambientColor{NoInit}, diffuseColor{NoInit}, specularColor{NoInit} {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref ambientColor field + * @return Reference to self (for method chaining) + */ + PhongMaterialUniform& setAmbientColor(const Color4& color) { + ambientColor = color; + return *this; + } + + /** + * @brief Set the @ref diffuseColor field + * @return Reference to self (for method chaining) + */ + PhongMaterialUniform& setDiffuseColor(const Color4& color) { + diffuseColor = color; + return *this; + } + + /** + * @brief Set the @ref specularColor field + * @return Reference to self (for method chaining) + */ + PhongMaterialUniform& setSpecularColor(const Color4& color) { + specularColor = color; + return *this; + } + + /** + * @brief Set the @ref normalTextureScale field + * @return Reference to self (for method chaining) + */ + PhongMaterialUniform& setNormalTextureScale(Float scale) { + normalTextureScale = scale; + return *this; + } + + /** + * @brief Set the @ref shininess field + * @return Reference to self (for method chaining) + */ + PhongMaterialUniform& setShininess(Float shininess) { + this->shininess = shininess; + return *this; + } + + /** + * @brief Set the @ref alphaMask field + * @return Reference to self (for method chaining) + */ + PhongMaterialUniform& setAlphaMask(Float alphaMask) { + this->alphaMask = alphaMask; + return *this; + } + + /** + * @} + */ + + /** + * @brief Ambient color + * + * Default value is @cpp 0x00000000_rgbaf @ce. If + * @ref PhongGL::Flag::AmbientTexture is enabled, be sure to set this field + * to @cpp 0xffffffff_rgbaf @ce, otherwise the texture will be ignored. + * + * If @ref PhongGL::Flag::VertexColor is enabled, the color is multiplied + * with a color coming from the @ref PhongGL::Color3 / @ref PhongGL::Color4 + * attribute. + * @see @ref PhongGL::setAmbientColor() + */ + Color4 ambientColor; + + /** + * @brief Diffuse color + * + * Default value is @cpp 0xffffffff_rgbaf @ce. + * + * Used only if @ref PhongDrawUniform::lightCount is not zero, ignored + * otherwise. If @ref PhongGL::Flag::VertexColor is enabled, the color is + * multiplied with a color coming from the @ref PhongGL::Color3 / + * @ref PhongGL::Color4 attribute. + * @see @ref PhongGL::setDiffuseColor() + */ + Color4 diffuseColor; + + /** + * @brief Specular color + * + * Default value is @cpp 0xffffff00_rgbaf @ce. + * + * Used only if @ref PhongDrawUniform::lightCount is not zero, ignored + * otherwise. + * @see @ref PhongGL::setSpecularColor() + */ + Color4 specularColor; + + /** + * @brief Normal texture scale + * + * Affects strength of the normal mapping. Default value is @cpp 1.0f @ce, + * meaning the normal texture is not changed in any way; a value of + * @cpp 0.0f @ce disables the normal texture effect altogether. + * + * Used only if @ref PhongGL::Flag::NormalTexture is enabled and + * @ref PhongDrawUniform::lightCount is not zero, ignored otherwise. + * @see @ref PhongGL::setNormalTextureScale() + */ + Float normalTextureScale; + + /** + * @brief Shininess + * + * The larger value, the harder surface (smaller specular highlight). + * Default value is @cpp 80.0f @ce. + * + * Used only if @ref PhongDrawUniform::lightCount is not zero, ignored + * otherwise. + * @see @ref PhongGL::setShininess() + */ + Float shininess; + + /** + * @brief Alpha mask value + * + * Fragments with alpha values smaller than the mask value will be + * discarded. Default value is @cpp 0.5f @ce. + * + * Used only if @ref PhongGL::Flag::AlphaMask is enabled, ignored + * otherwise. + * @see @ref PhongGL::setAlphaMask() + */ + Float alphaMask; + + /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + Int:32; + #endif +}; + +/** +@brief Light parameters for Phong shaders +@m_since_latest + +Describes light properties referenced by the @ref PhongDrawUniform::lightOffset +and @ref PhongDrawUniform::lightCount range. +@see @ref PhongGL::bindLightBuffer() +*/ +struct PhongLightUniform { + /** @brief Construct with default parameters */ + constexpr explicit PhongLightUniform(DefaultInitT = DefaultInit) noexcept: position{0.0f, 0.0f, 1.0f, 0.0f}, color{1.0f, 1.0f, 1.0f}, specularColor{1.0f, 1.0f, 1.0f}, range{Constants::inf()} {} + /** @brief Construct without initializing the contents */ + explicit PhongLightUniform(NoInitT) noexcept: position{NoInit}, color{NoInit}, specularColor{NoInit} {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref position field + * @return Reference to self (for method chaining) + */ + PhongLightUniform& setPosition(const Vector4& position) { + this->position = position; + return *this; + } + + /** + * @brief Set the @ref color field + * @return Reference to self (for method chaining) + */ + PhongLightUniform& setColor(const Color3& color) { + this->color = color; + return *this; + } + + /** + * @brief Set the @ref specularColor field + * @return Reference to self (for method chaining) + */ + PhongLightUniform& setSpecularColor(const Color3& specularColor) { + this->specularColor = specularColor; + return *this; + } + + /** + * @brief Set the @ref range field + * @return Reference to self (for method chaining) + */ + PhongLightUniform& setRange(Float range) { + this->range = range; + return *this; + } + + /** + * @} + */ + + /** + * @brief Position + * + * Depending on the fourth component, the value is treated as either a + * camera-relative position of a point light, if the fourth component is + * @cpp 1.0f @ce; or a direction *to* a directional light, if the fourth + * component is @cpp 0.0f @ce. Default value is + * @cpp {0.0f, 0.0f, 1.0f, 0.0f} @ce --- a directional "fill" light coming + * from the camera. + * @see @ref PhongGL::setLightPositions() + */ + Vector4 position; + + /** + * @brief Color + * + * Default value is @cpp 0xffffff_rgbf @ce. + * @see @ref PhongGL::setLightColors() + */ + Color3 color; + + /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + Int:32; /* reserved for cone inner angle */ + #endif + + /** + * @brief Specular color + * + * Usually you'd set this value to the same as @ref color, but it allows + * for greater flexibility such as disabling specular highlights on certain + * lights. Default value is @cpp 0xffffff_rgbf @ce. + * @see @ref PhongGL::setLightColors() + */ + Color3 specularColor; + + /* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + Int:32; /* reserved for cone outer angle */ + #endif + + /** + * @brief Attenuation range + * + * Default value is @ref Constants::inf(). + * @see @ref PhongGL::setLightRanges() + */ + Float range; + + /* warning: Member __pad2__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + Int:32; /* reserved for cone direction */ + Int:32; + Int:32; + #endif +}; + +#ifdef MAGNUM_BUILD_DEPRECATED /** @brief @copybrief PhongGL * @m_deprecated_since_latest Use @ref PhongGL instead. */ typedef CORRADE_DEPRECATED("use PhongGL instead") PhongGL Phong; +#endif }} -#else -#error use Magnum/Shaders/PhongGL.h and the PhongGL class instead -#endif #endif diff --git a/src/Magnum/Shaders/Phong.vert b/src/Magnum/Shaders/Phong.vert index 80f48ce04..d4a4c5514 100644 --- a/src/Magnum/Shaders/Phong.vert +++ b/src/Magnum/Shaders/Phong.vert @@ -32,8 +32,13 @@ #define out varying #endif +#ifndef RUNTIME_CONST +#define const +#endif + /* Uniforms */ +#ifndef UNIFORM_BUFFERS #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -86,6 +91,92 @@ uniform highp vec4 lightPositions[LIGHT_COUNT] ; #endif +/* Uniform buffers */ + +#else +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 0) +#endif +uniform highp uint drawOffset + #ifndef GL_ES + = 0u + #endif + ; + +/* Keep in sync with Phong.frag. Can't "outsource" to a common file because + the #extension directive needs to be always before any code. */ +struct DrawUniform { + mediump mat3 normalMatrix; /* actually mat3x4 */ + highp uvec4 materialIdReservedObjectIdLightOffsetLightCount; + #define draw_materialIdReserved materialIdReservedObjectIdLightOffsetLightCount.x + #define draw_objectId materialIdReservedObjectIdLightOffsetLightCount.y + #define draw_lightOffset materialIdReservedObjectIdLightOffsetLightCount.z + #define draw_lightCount materialIdReservedObjectIdLightOffsetLightCount.w +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 2 + #endif +) uniform Draw { + DrawUniform draws[DRAW_COUNT]; +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 0 + #endif +) uniform Projection { + highp mat4 projectionMatrix; +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 1 + #endif +) uniform Transformation { + highp mat4 transformationMatrices[DRAW_COUNT]; +}; + +#ifdef TEXTURE_TRANSFORMATION +struct TextureTransformationUniform { + highp vec4 rotationScaling; + highp vec4 offsetReservedReserved; + #define textureTransformation_offset offsetReservedReserved.xy +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 3 + #endif +) uniform TextureTransformation { + TextureTransformationUniform textureTransformations[DRAW_COUNT]; +}; +#endif + +#if LIGHT_COUNT +/* Keep in sync with Phong.frag. Can't "outsource" to a common file because + the #extension directive needs to be always before any code. */ +struct LightUniform { + highp vec4 position; + lowp vec3 colorReserved; + #define light_color colorReserved.xyz + lowp vec4 specularColorReserved; + #define light_specularColor specularColorReserved.xyz + lowp vec4 rangeReservedReservedReserved; + #define light_range rangeReservedReservedReserved.x +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 5 + #endif +) uniform Light { + LightUniform lights[LIGHT_COUNT]; +}; +#endif +#endif + /* Inputs */ #ifdef EXPLICIT_ATTRIB_LOCATION @@ -183,6 +274,19 @@ out highp vec3 cameraDirection; #endif void main() { + #ifdef UNIFORM_BUFFERS + highp const mat4 transformationMatrix = transformationMatrices[drawOffset]; + #if LIGHT_COUNT + mediump const mat3 normalMatrix = draws[drawOffset].normalMatrix; + #endif + #ifdef TEXTURE_TRANSFORMATION + mediump const mat3 textureMatrix = mat3(textureTransformations[drawOffset].rotationScaling.xy, 0.0, textureTransformations[drawOffset].rotationScaling.zw, 0.0, textureTransformations[drawOffset].textureTransformation_offset, 1.0); + #endif + #if LIGHT_COUNT + mediump const uint lightOffset = draws[drawOffset].draw_lightOffset; + #endif + #endif + /* Transformed vertex position */ highp vec4 transformedPosition4 = transformationMatrix* #ifdef INSTANCED_TRANSFORMATION @@ -221,8 +325,21 @@ void main() { /* Direction to the light. Directional lights have the last component set to 0, which gets used to ignore the transformed position. */ + #ifndef UNIFORM_BUFFERS for(int i = 0; i < LIGHT_COUNT; ++i) - lightDirections[i] = vec4(lightPositions[i].xyz - transformedPosition*lightPositions[i].w, lightPositions[i].w); + #else + for(uint i = 0u, actualLightCount = min(uint(LIGHT_COUNT), draws[drawOffset].draw_lightCount); i < actualLightCount; ++i) + #endif + { + highp const vec4 lightPosition = + #ifndef UNIFORM_BUFFERS + lightPositions[i] + #else + lights[lightOffset + i].position + #endif + ; + lightDirections[i] = vec4(lightPosition.xyz - transformedPosition*lightPosition.w, lightPosition.w); + } /* Direction to the camera */ cameraDirection = -transformedPosition; diff --git a/src/Magnum/Shaders/PhongGL.cpp b/src/Magnum/Shaders/PhongGL.cpp index 750c6c2ad..87d62e4bd 100644 --- a/src/Magnum/Shaders/PhongGL.cpp +++ b/src/Magnum/Shaders/PhongGL.cpp @@ -43,6 +43,10 @@ #include "Magnum/Math/Matrix3.h" #include "Magnum/Math/Matrix4.h" +#ifndef MAGNUM_TARGET_GLES2 +#include "Magnum/GL/Buffer.h" +#endif + #include "Magnum/Shaders/Implementation/CreateCompatibilityShader.h" namespace Magnum { namespace Shaders { @@ -54,12 +58,49 @@ namespace { SpecularTextureUnit = 2, NormalTextureUnit = 3 }; + + #ifndef MAGNUM_TARGET_GLES2 + enum: Int { + ProjectionBufferBinding = 0, + TransformationBufferBinding = 1, + DrawBufferBinding = 2, + TextureTransformationBufferBinding = 3, + MaterialBufferBinding = 4, + LightBufferBinding = 5 + }; + #endif } -PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _lightCount{lightCount}, _lightColorsUniform{_lightPositionsUniform + Int(lightCount)}, _lightSpecularColorsUniform{_lightPositionsUniform + 2*Int(lightCount)}, _lightRangesUniform{_lightPositionsUniform + 3*Int(lightCount)} { +PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount + #ifndef MAGNUM_TARGET_GLES2 + , const UnsignedInt materialCount, const UnsignedInt drawCount + #endif +): + _flags{flags}, + _lightCount{lightCount}, + #ifndef MAGNUM_TARGET_GLES2 + _materialCount{materialCount}, + _drawCount{drawCount}, + #endif + _lightColorsUniform{_lightPositionsUniform + Int(lightCount)}, + _lightSpecularColorsUniform{_lightPositionsUniform + 2*Int(lightCount)}, + _lightRangesUniform{_lightPositionsUniform + 3*Int(lightCount)} +{ CORRADE_ASSERT(!(flags & Flag::TextureTransformation) || (flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture)), "Shaders::PhongGL: texture transformation enabled but the shader is not textured", ); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || materialCount, + "Shaders::PhongGL: material count can't be zero", ); + CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || drawCount, + "Shaders::PhongGL: draw count can't be zero", ); + #endif + + #ifndef MAGNUM_TARGET_GLES + if(flags >= Flag::UniformBuffers) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); + #endif + #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ if(!Utility::Resource::hasGroup("MagnumShadersGL")) @@ -69,6 +110,11 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount): _flags{flags} const GL::Context& context = GL::Context::current(); + #ifndef MAGNUM_TARGET_GLES + CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || context.isExtensionSupported(), + "Shaders::PhongGL: uniform buffers require" << GL::Extensions::ARB::uniform_buffer_object::string(), ); + #endif + #ifndef MAGNUM_TARGET_GLES const GL::Version version = context.supportedVersion({GL::Version::GL320, GL::Version::GL310, GL::Version::GL300, GL::Version::GL210}); #else @@ -80,7 +126,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount): _flags{flags} #ifndef MAGNUM_TARGET_GLES std::string lightInitializerVertex, lightInitializerFragment; - if(lightCount) { + if(!(flags >= Flag::UniformBuffers) && lightCount) { using namespace Containers::Literals; /* Initializer for the light color / position / range arrays -- we need @@ -140,8 +186,19 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount): _flags{flags} #endif .addSource(flags & Flag::InstancedTransformation ? "#define INSTANCED_TRANSFORMATION\n" : "") .addSource(flags >= Flag::InstancedTextureOffset ? "#define INSTANCED_TEXTURE_OFFSET\n" : ""); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + vert.addSource(Utility::formatString( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n" + "#define LIGHT_COUNT {}\n", + drawCount, + lightCount)); + } + #endif #ifndef MAGNUM_TARGET_GLES - if(lightCount) vert.addSource(std::move(lightInitializerVertex)); + if(!(flags >= Flag::UniformBuffers) && lightCount) + vert.addSource(std::move(lightInitializerVertex)); #endif vert.addSource(rs.get("generic.glsl")) .addSource(rs.get("Phong.vert")); @@ -156,7 +213,21 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount): _flags{flags} .addSource(flags & Flag::ObjectId ? "#define OBJECT_ID\n" : "") .addSource(flags >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") #endif - .addSource(Utility::formatString( + ; + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + frag.addSource(Utility::formatString( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n" + "#define MATERIAL_COUNT {}\n" + "#define LIGHT_COUNT {}\n", + drawCount, + materialCount, + lightCount)); + } else + #endif + { + frag.addSource(Utility::formatString( "#define LIGHT_COUNT {}\n" "#define LIGHT_COLORS_LOCATION {}\n" "#define LIGHT_SPECULAR_COLORS_LOCATION {}\n" @@ -165,8 +236,10 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount): _flags{flags} _lightPositionsUniform + lightCount, _lightPositionsUniform + 2*lightCount, _lightPositionsUniform + 3*lightCount)); + } #ifndef MAGNUM_TARGET_GLES - if(lightCount) frag.addSource(std::move(lightInitializerFragment)); + if(!(flags >= Flag::UniformBuffers) && lightCount) + frag.addSource(std::move(lightInitializerFragment)); #endif frag.addSource(rs.get("generic.glsl")) .addSource(rs.get("Phong.frag")); @@ -215,27 +288,34 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount): _flags{flags} if(!context.isExtensionSupported(version)) #endif { - _transformationMatrixUniform = uniformLocation("transformationMatrix"); - if(flags & Flag::TextureTransformation) - _textureMatrixUniform = uniformLocation("textureMatrix"); - _projectionMatrixUniform = uniformLocation("projectionMatrix"); - _ambientColorUniform = uniformLocation("ambientColor"); - if(lightCount) { - _normalMatrixUniform = uniformLocation("normalMatrix"); - _diffuseColorUniform = uniformLocation("diffuseColor"); - _specularColorUniform = uniformLocation("specularColor"); - _shininessUniform = uniformLocation("shininess"); - if(flags & Flag::NormalTexture) - _normalTextureScaleUniform = uniformLocation("normalTextureScale"); - _lightPositionsUniform = uniformLocation("lightPositions"); - _lightColorsUniform = uniformLocation("lightColors"); - _lightSpecularColorsUniform = uniformLocation("lightSpecularColors"); - _lightRangesUniform = uniformLocation("lightRanges"); - } - if(flags & Flag::AlphaMask) _alphaMaskUniform = uniformLocation("alphaMask"); #ifndef MAGNUM_TARGET_GLES2 - if(flags & Flag::ObjectId) _objectIdUniform = uniformLocation("objectId"); + if(flags >= Flag::UniformBuffers) { + _drawOffsetUniform = uniformLocation("drawOffset"); + } else #endif + { + _transformationMatrixUniform = uniformLocation("transformationMatrix"); + if(flags & Flag::TextureTransformation) + _textureMatrixUniform = uniformLocation("textureMatrix"); + _projectionMatrixUniform = uniformLocation("projectionMatrix"); + _ambientColorUniform = uniformLocation("ambientColor"); + if(lightCount) { + _normalMatrixUniform = uniformLocation("normalMatrix"); + _diffuseColorUniform = uniformLocation("diffuseColor"); + _specularColorUniform = uniformLocation("specularColor"); + _shininessUniform = uniformLocation("shininess"); + if(flags & Flag::NormalTexture) + _normalTextureScaleUniform = uniformLocation("normalTextureScale"); + _lightPositionsUniform = uniformLocation("lightPositions"); + _lightColorsUniform = uniformLocation("lightColors"); + _lightSpecularColorsUniform = uniformLocation("lightSpecularColors"); + _lightRangesUniform = uniformLocation("lightRanges"); + } + if(flags & Flag::AlphaMask) _alphaMaskUniform = uniformLocation("alphaMask"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags & Flag::ObjectId) _objectIdUniform = uniformLocation("objectId"); + #endif + } } #ifndef MAGNUM_TARGET_GLES @@ -248,57 +328,100 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount): _flags{flags} if(flags & Flag::SpecularTexture) setUniform(uniformLocation("specularTexture"), SpecularTextureUnit); if(flags & Flag::NormalTexture) setUniform(uniformLocation("normalTexture"), NormalTextureUnit); } + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + setUniformBlockBinding(uniformBlockIndex("Projection"), ProjectionBufferBinding); + setUniformBlockBinding(uniformBlockIndex("Transformation"), TransformationBufferBinding); + setUniformBlockBinding(uniformBlockIndex("Draw"), DrawBufferBinding); + setUniformBlockBinding(uniformBlockIndex("Material"), MaterialBufferBinding); + if(flags & Flag::TextureTransformation) + setUniformBlockBinding(uniformBlockIndex("TextureTransformation"), TextureTransformationBufferBinding); + if(lightCount) + setUniformBlockBinding(uniformBlockIndex("Light"), LightBufferBinding); + } + #endif } /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ #ifdef MAGNUM_TARGET_GLES - /* Default to fully opaque white so we can see the textures */ - if(flags & Flag::AmbientTexture) setAmbientColor(Magnum::Color4{1.0f}); - else setAmbientColor(Magnum::Color4{0.0f}); - setTransformationMatrix(Matrix4{Math::IdentityInit}); - setProjectionMatrix(Matrix4{Math::IdentityInit}); - if(lightCount) { - setDiffuseColor(Magnum::Color4{1.0f}); - setSpecularColor(Magnum::Color4{1.0f, 0.0f}); - setShininess(80.0f); - if(flags & Flag::NormalTexture) - setNormalTextureScale(1.0f); - setLightPositions(Containers::Array{DirectInit, lightCount, Vector4{0.0f, 0.0f, 1.0f, 0.0f}}); - Containers::Array colors{DirectInit, lightCount, Magnum::Color3{1.0f}}; - setLightColors(colors); - setLightSpecularColors(colors); - setLightRanges(Containers::Array{DirectInit, lightCount, Constants::inf()}); - /* Light position is zero by default */ - setNormalMatrix(Matrix3x3{Math::IdentityInit}); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + /* Draw offset is zero by default */ + } else + #endif + { + /* Default to fully opaque white so we can see the textures */ + if(flags & Flag::AmbientTexture) setAmbientColor(Magnum::Color4{1.0f}); + else setAmbientColor(Magnum::Color4{0.0f}); + setTransformationMatrix(Matrix4{Math::IdentityInit}); + setProjectionMatrix(Matrix4{Math::IdentityInit}); + if(lightCount) { + setDiffuseColor(Magnum::Color4{1.0f}); + setSpecularColor(Magnum::Color4{1.0f, 0.0f}); + setShininess(80.0f); + if(flags & Flag::NormalTexture) + setNormalTextureScale(1.0f); + setLightPositions(Containers::Array{DirectInit, lightCount, Vector4{0.0f, 0.0f, 1.0f, 0.0f}}); + Containers::Array colors{DirectInit, lightCount, Magnum::Color3{1.0f}}; + setLightColors(colors); + setLightSpecularColors(colors); + setLightRanges(Containers::Array{DirectInit, lightCount, Constants::inf()}); + /* Light position is zero by default */ + setNormalMatrix(Matrix3x3{Math::IdentityInit}); + } + if(flags & Flag::TextureTransformation) + setTextureMatrix(Matrix3{Math::IdentityInit}); + if(flags & Flag::AlphaMask) setAlphaMask(0.5f); + /* Object ID is zero by default */ } - if(flags & Flag::TextureTransformation) - setTextureMatrix(Matrix3{Math::IdentityInit}); - if(flags & Flag::AlphaMask) setAlphaMask(0.5f); - /* Object ID is zero by default */ #endif } +#ifndef MAGNUM_TARGET_GLES2 +PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount): PhongGL{flags, lightCount, 1, 1} {} +#endif + PhongGL& PhongGL::setAmbientColor(const Magnum::Color4& color) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setAmbientColor(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_ambientColorUniform, color); return *this; } PhongGL& PhongGL::setDiffuseColor(const Magnum::Color4& color) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setDiffuseColor(): the shader was created with uniform buffers enabled", *this); + #endif if(_lightCount) setUniform(_diffuseColorUniform, color); return *this; } PhongGL& PhongGL::setSpecularColor(const Magnum::Color4& color) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setSpecularColor(): the shader was created with uniform buffers enabled", *this); + #endif if(_lightCount) setUniform(_specularColorUniform, color); return *this; } PhongGL& PhongGL::setShininess(Float shininess) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setShininess(): the shader was created with uniform buffers enabled", *this); + #endif if(_lightCount) setUniform(_shininessUniform, shininess); return *this; } PhongGL& PhongGL::setNormalTextureScale(const Float scale) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setNormalTextureScale(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(_flags & Flag::NormalTexture, "Shaders::PhongGL::setNormalTextureScale(): the shader was not created with normal texture enabled", *this); if(_lightCount) setUniform(_normalTextureScaleUniform, scale); @@ -306,6 +429,10 @@ PhongGL& PhongGL::setNormalTextureScale(const Float scale) { } PhongGL& PhongGL::setAlphaMask(Float mask) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setAlphaMask(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(_flags & Flag::AlphaMask, "Shaders::PhongGL::setAlphaMask(): the shader was not created with alpha mask enabled", *this); setUniform(_alphaMaskUniform, mask); @@ -314,6 +441,8 @@ PhongGL& PhongGL::setAlphaMask(Float mask) { #ifndef MAGNUM_TARGET_GLES2 PhongGL& PhongGL::setObjectId(UnsignedInt id) { + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setObjectId(): the shader was created with uniform buffers enabled", *this); CORRADE_ASSERT(_flags & Flag::ObjectId, "Shaders::PhongGL::setObjectId(): the shader was not created with object ID enabled", *this); setUniform(_objectIdUniform, id); @@ -322,21 +451,37 @@ PhongGL& PhongGL::setObjectId(UnsignedInt id) { #endif PhongGL& PhongGL::setTransformationMatrix(const Matrix4& matrix) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setTransformationMatrix(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_transformationMatrixUniform, matrix); return *this; } PhongGL& PhongGL::setNormalMatrix(const Matrix3x3& matrix) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setNormalMatrix(): the shader was created with uniform buffers enabled", *this); + #endif if(_lightCount) setUniform(_normalMatrixUniform, matrix); return *this; } PhongGL& PhongGL::setProjectionMatrix(const Matrix4& matrix) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setProjectionMatrix(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_projectionMatrixUniform, matrix); return *this; } PhongGL& PhongGL::setTextureMatrix(const Matrix3& matrix) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setTextureMatrix(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(_flags & Flag::TextureTransformation, "Shaders::PhongGL::setTextureMatrix(): the shader was not created with texture transformation enabled", *this); setUniform(_textureMatrixUniform, matrix); @@ -344,6 +489,10 @@ PhongGL& PhongGL::setTextureMatrix(const Matrix3& matrix) { } PhongGL& PhongGL::setLightPositions(const Containers::ArrayView positions) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setLightPositions(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(_lightCount == positions.size(), "Shaders::PhongGL::setLightPositions(): expected" << _lightCount << "items but got" << positions.size(), *this); if(_lightCount) setUniform(_lightPositionsUniform, positions); @@ -373,6 +522,10 @@ PhongGL& PhongGL::setLightPositions(const std::initializer_list positio #endif PhongGL& PhongGL::setLightPosition(const UnsignedInt id, const Vector4& position) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setLightPosition(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(id < _lightCount, "Shaders::PhongGL::setLightPosition(): light ID" << id << "is out of bounds for" << _lightCount << "lights", *this); setUniform(_lightPositionsUniform + id, position); @@ -391,6 +544,10 @@ PhongGL& PhongGL::setLightPosition(const Vector3& position) { #endif PhongGL& PhongGL::setLightColors(const Containers::ArrayView colors) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setLightColors(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(_lightCount == colors.size(), "Shaders::PhongGL::setLightColors(): expected" << _lightCount << "items but got" << colors.size(), *this); if(_lightCount) setUniform(_lightColorsUniform, colors); @@ -418,6 +575,10 @@ PhongGL& PhongGL::setLightColors(const std::initializer_list col } PhongGL& PhongGL::setLightColor(const UnsignedInt id, const Magnum::Color3& color) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setLightColor(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(id < _lightCount, "Shaders::PhongGL::setLightColor(): light ID" << id << "is out of bounds for" << _lightCount << "lights", *this); setUniform(_lightColorsUniform + id, color); @@ -436,6 +597,10 @@ PhongGL& PhongGL::setLightColor(const Magnum::Color4& color) { #endif PhongGL& PhongGL::setLightSpecularColors(const Containers::ArrayView colors) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setLightSpecularColors(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(_lightCount == colors.size(), "Shaders::PhongGL::setLightSpecularColors(): expected" << _lightCount << "items but got" << colors.size(), *this); if(_lightCount) setUniform(_lightSpecularColorsUniform, colors); @@ -447,6 +612,10 @@ PhongGL& PhongGL::setLightSpecularColors(const std::initializer_list= Flag::UniformBuffers), + "Shaders::PhongGL::setLightSpecularColor(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(id < _lightCount, "Shaders::PhongGL::setLightSpecularColor(): light ID" << id << "is out of bounds for" << _lightCount << "lights", *this); setUniform(_lightSpecularColorsUniform + id, color); @@ -454,6 +623,10 @@ PhongGL& PhongGL::setLightSpecularColor(const UnsignedInt id, const Magnum::Colo } PhongGL& PhongGL::setLightRanges(const Containers::ArrayView ranges) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setLightRanges(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(_lightCount == ranges.size(), "Shaders::PhongGL::setLightRanges(): expected" << _lightCount << "items but got" << ranges.size(), *this); if(_lightCount) setUniform(_lightRangesUniform, ranges); @@ -465,12 +638,115 @@ PhongGL& PhongGL::setLightRanges(const std::initializer_list ranges) { } PhongGL& PhongGL::setLightRange(const UnsignedInt id, const Float range) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::PhongGL::setLightRange(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(id < _lightCount, "Shaders::PhongGL::setLightRange(): light ID" << id << "is out of bounds for" << _lightCount << "lights", *this); setUniform(_lightRangesUniform + id, range); return *this; } +#ifndef MAGNUM_TARGET_GLES2 +PhongGL& PhongGL::setDrawOffset(const UnsignedInt offset) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::PhongGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(offset < _drawCount, + "Shaders::PhongGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); + setUniform(_drawOffsetUniform, offset); + return *this; +} + +PhongGL& PhongGL::bindProjectionBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::PhongGL::bindProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, ProjectionBufferBinding); + return *this; +} + +PhongGL& PhongGL::bindProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::PhongGL::bindProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, ProjectionBufferBinding, offset, size); + return *this; +} + +PhongGL& PhongGL::bindTransformationBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::PhongGL::bindTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TransformationBufferBinding); + return *this; +} + +PhongGL& PhongGL::bindTransformationBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::PhongGL::bindTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TransformationBufferBinding, offset, size); + return *this; +} + +PhongGL& PhongGL::bindDrawBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::PhongGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding); + return *this; +} + +PhongGL& PhongGL::bindDrawBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::PhongGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); + return *this; +} + +PhongGL& PhongGL::bindTextureTransformationBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::PhongGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureTransformation, + "Shaders::PhongGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding); + return *this; +} + +PhongGL& PhongGL::bindTextureTransformationBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::PhongGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureTransformation, + "Shaders::PhongGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); + return *this; +} + +PhongGL& PhongGL::bindMaterialBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::PhongGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding); + return *this; +} + +PhongGL& PhongGL::bindMaterialBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::PhongGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); + return *this; +} + +PhongGL& PhongGL::bindLightBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::PhongGL::bindLightBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, LightBufferBinding); + return *this; +} + +PhongGL& PhongGL::bindLightBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::PhongGL::bindLightBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, LightBufferBinding, offset, size); + return *this; +} +#endif + PhongGL& PhongGL::bindAmbientTexture(GL::Texture2D& texture) { CORRADE_ASSERT(_flags & Flag::AmbientTexture, "Shaders::PhongGL::bindAmbientTexture(): the shader was not created with ambient texture enabled", *this); @@ -526,6 +802,9 @@ Debug& operator<<(Debug& debug, const PhongGL::Flag value) { #endif _c(InstancedTransformation) _c(InstancedTextureOffset) + #ifndef MAGNUM_TARGET_GLES2 + _c(UniformBuffers) + #endif #undef _c /* LCOV_EXCL_STOP */ } @@ -548,7 +827,11 @@ Debug& operator<<(Debug& debug, const PhongGL::Flags value) { PhongGL::Flag::InstancedObjectId, /* Superset of ObjectId */ PhongGL::Flag::ObjectId, #endif - PhongGL::Flag::InstancedTransformation}); + PhongGL::Flag::InstancedTransformation, + #ifndef MAGNUM_TARGET_GLES2 + PhongGL::Flag::UniformBuffers + #endif + }); } }} diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index 66547b5cd..cb67a8a02 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -509,9 +509,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { /** * Instanced object ID. Retrieves a per-instance / per-vertex * object ID from the @ref ObjectId attribute, outputting a sum of - * the per-vertex ID and ID coming from @ref setObjectId(). - * Implicitly enables @ref Flag::ObjectId. See - * @ref Shaders-PhongGL-object-id for more information. + * the per-vertex ID and ID coming from @ref setObjectId() or + * @ref PhongDrawUniform::objectId. Implicitly enables + * @ref Flag::ObjectId. See @ref Shaders-PhongGL-object-id for more + * information. * @requires_gl30 Extension @gl_extension{EXT,gpu_shader4} * @requires_gles30 Object ID output requires integer support in * shaders, which is not available in OpenGL ES 2.0 or WebGL @@ -526,9 +527,11 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * transformation and normal matrix from the * @ref TransformationMatrix / @ref NormalMatrix attributes and * uses them together with matrices coming from - * @ref setTransformationMatrix() and @ref setNormalMatrix() (first - * the per-instance, then the uniform matrix). See - * @ref Shaders-PhongGL-instancing for more information. + * @ref setTransformationMatrix() and @ref setNormalMatrix() or + * @ref TransformationUniform3D::transformationMatrix and + * @ref PhongDrawUniform::normalMatrix (first the per-instance, + * then the uniform matrix). See @ref Shaders-PhongGL-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 @@ -542,7 +545,9 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { /** * 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 + * the matrix coming from @ref setTextureMatrix() or + * @ref TextureTransformationUniform::rotationScaling and + * @ref TextureTransformationUniform::offset (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(). @@ -556,7 +561,24 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * in WebGL 1.0. * @m_since{2020,06} */ - InstancedTextureOffset = (1 << 10)|TextureTransformation + InstancedTextureOffset = (1 << 10)|TextureTransformation, + + #ifndef MAGNUM_TARGET_GLES2 + /** + * Use uniform buffers. Expects that uniform data are supplied via + * @ref bindProjectionBuffer(), @ref bindTransformationBuffer(), + * @ref bindDrawBuffer(), @ref bindTextureTransformationBuffer(), + * @ref bindMaterialBuffer() and @ref bindLightBuffer() instead of + * direct uniform setters. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES + * 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL + * 1.0. + * @m_since_latest + */ + UniformBuffers = 1 << 12 + #endif }; /** @@ -570,9 +592,54 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @brief Constructor * @param flags Flags * @param lightCount Count of light sources + * + * While this function is meant mainly for the classic uniform + * scenario (without @ref Flag::UniformBuffers set), it's equivalent to + * @ref PhongGL(Flags, UnsignedInt, UnsignedInt, UnsignedInt) with + * @p materialCount and @p drawCount set to @cpp 1 @ce. */ explicit PhongGL(Flags flags = {}, UnsignedInt lightCount = 1); + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Construct for a multi-draw scenario + * @param flags Flags + * @param lightCount Size of a @ref PhongLightUniform buffer bound + * with @ref bindLightBuffer() + * @param materialCount Size of a @ref PhongMaterialUniform buffer + * bound with @ref bindMaterialBuffer() + * @param drawCount Size of a @ref ProjectionUniform3D / + * @ref TransformationUniform3D / @ref PhongDrawUniform / + * @ref TextureTransformationUniform buffer bound with + * @ref bindProjectionBuffer(), @ref bindTransformationBuffer(), + * @ref bindDrawBuffer() and @ref bindTextureTransformationBuffer() + * + * If @p flags contains @ref Flag::UniformBuffers, @p lightCount, + * @p materialCount and @p drawCount describe the uniform buffer sizes + * as these are required to have a statically defined size. The draw + * offset is then set via @ref setDrawOffset() and the per-draw + * materials and lights are specified via + * @ref PhongDrawUniform::materialId, + * @ref PhongDrawUniform::lightOffset and + * @ref PhongDrawUniform::lightCount. + * + * If @p flags don't contain @ref Flag::UniformBuffers, + * @p materialCount and @p drawCount is ignored and the constructor + * behaves the same as @ref PhongGL(Flags, UnsignedInt). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + /** @todo this constructor will eventually need to have also joint + count, per-vertex weight count, view count for multiview and clip + plane count ... and putting them in arbitrary order next to each + other is too error-prone, so it needs some other solution + (accepting pairs of parameter type and value like in GL context + creation, e.g., which will probably need a new enum as reusing Flag + for this might be too confusing) */ + explicit PhongGL(Flags flags, UnsignedInt lightCount, UnsignedInt materialCount, UnsignedInt drawCount); + #endif + /** * @brief Construct without creating the underlying OpenGL object * @@ -602,11 +669,46 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { /** @brief Flags */ Flags flags() const { return _flags; } - /** @brief Light count */ + /** + * @brief Light count + * + * If @ref Flag::UniformBuffers is set, this is the statically defined + * size of the @ref PhongLightUniform uniform buffer. + * @see @ref bindLightBuffer() + */ UnsignedInt lightCount() const { return _lightCount; } + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Material count + * @m_since_latest + * + * Statically defined size of the @ref PhongMaterialUniform uniform + * buffer. Has use only if @ref Flag::UniformBuffers is set. + * @see @ref bindMaterialBuffer() + * @requires_gles30 Not defined on OpenGL ES 2.0 builds. + * @requires_webgl20 Not defined on WebGL 1.0 builds. + */ + UnsignedInt materialCount() const { return _materialCount; } + + /** + * @brief Draw count + * @m_since_latest + * + * Statically defined size of each of the @ref ProjectionUniform3D, + * @ref TransformationUniform3D, @ref PhongDrawUniform and + * @ref TextureTransformationUniform uniform buffers. Has use only if + * @ref Flag::UniformBuffers is set. + * @requires_gles30 Not defined on OpenGL ES 2.0 builds. + * @requires_webgl20 Not defined on WebGL 1.0 builds. + */ + UnsignedInt drawCount() const { return _drawCount; } + #endif + /** @{ * @name Uniform setters + * + * Used only if @ref Flag::UniformBuffers is not set. */ /** @@ -618,6 +720,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * ambient texture, otherwise default value is @cpp 0x00000000_rgbaf @ce. * If @ref Flag::VertexColor is set, the color is multiplied with a * color coming from the @ref Color3 / @ref Color4 attribute. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref PhongMaterialUniform::ambientColor and call + * @ref bindMaterialBuffer() instead. * @see @ref bindAmbientTexture(), @ref Shaders-PhongGL-lights-ambient */ PhongGL& setAmbientColor(const Magnum::Color4& color); @@ -632,6 +738,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * as diffuse color doesn't contribute to the output in that case. * If @ref Flag::VertexColor is set, the color is multiplied with a * color coming from the @ref Color3 / @ref Color4 attribute. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref PhongMaterialUniform::diffuseColor and call + * @ref bindMaterialBuffer() instead. * @see @ref bindDiffuseTexture() */ PhongGL& setDiffuseColor(const Magnum::Color4& color); @@ -649,6 +759,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * Expects that the shader was created with @ref Flag::NormalTexture * enabled. If @ref lightCount() is zero, this function is a no-op, as * normals don't contribute to the output in that case. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref PhongMaterialUniform::normalTextureScale and call + * @ref bindDrawBuffer() instead. * @see @ref Shaders-PhongGL-normal-mapping, @ref bindNormalTexture(), * @ref Trade::MaterialAttribute::NormalTextureScale */ @@ -664,6 +778,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * the specular color to @cpp 0x00000000_rgbaf @ce. If * @ref lightCount() is zero, this function is a no-op, as specular * color doesn't contribute to the output in that case. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref PhongMaterialUniform::specularColor and call + * @ref bindMaterialBuffer() instead. * @see @ref bindSpecularTexture() */ PhongGL& setSpecularColor(const Magnum::Color4& color); @@ -676,6 +794,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * Initial value is @cpp 80.0f @ce. If @ref lightCount() is zero, this * function is a no-op, as specular color doesn't contribute to the * output in that case. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref PhongMaterialUniform::shininess and call + * @ref bindMaterialBuffer() instead. */ PhongGL& setShininess(Float shininess); @@ -690,6 +812,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * * This corresponds to @m_class{m-doc-external} [glAlphaFunc()](https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glAlphaFunc.xml) * in classic OpenGL. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref PhongMaterialUniform::alphaMask and call + * @ref bindMaterialBuffer() instead. * @m_keywords{glAlphaFunc()} */ PhongGL& setAlphaMask(Float mask); @@ -704,6 +830,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @ref Shaders-PhongGL-object-id for more information. Default is * @cpp 0 @ce. If @ref Flag::InstancedObjectId is enabled as well, this * value is added to the ID coming from the @ref ObjectId attribute. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref PhongDrawUniform::objectId and call @ref bindDrawBuffer() + * instead. * @requires_gl30 Extension @gl_extension{EXT,gpu_shader4} * @requires_gles30 Object ID output requires integer support in * shaders, which is not available in OpenGL ES 2.0 or WebGL 1.0. @@ -720,6 +850,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @ref Flag::InstancedTransformation is set, the per-instance * transformation coming from the @ref TransformationMatrix attribute * is applied first, before this one. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref TransformationUniform3D::transformationMatrix and call + * @ref bindTransformationBuffer() instead. */ PhongGL& setTransformationMatrix(const Matrix4& matrix); @@ -735,6 +869,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * that case. If @ref Flag::InstancedTransformation is set, the * per-instance normal matrix coming from the @ref NormalMatrix * attribute is applied first, before this one. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref PhongDrawUniform::normalMatrix and call + * @ref bindDrawBuffer() instead. * @see @ref Math::Matrix4::normalMatrix() */ PhongGL& setNormalMatrix(const Matrix3x3& matrix); @@ -746,6 +884,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * Initial value is an identity matrix (i.e., an orthographic * projection of the default @f$ [ -\boldsymbol{1} ; \boldsymbol{1} ] @f$ * cube). + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref ProjectionUniform3D::projectionMatrix and call + * @ref bindProjectionBuffer() instead. */ PhongGL& setProjectionMatrix(const Matrix4& matrix); @@ -759,6 +901,11 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * identity matrix. If @ref Flag::InstancedTextureOffset is set, the * per-instance offset coming from the @ref TextureOffset atttribute is * applied first, before this matrix. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref TextureTransformationUniform::rotationScaling and + * @ref TextureTransformationUniform::offset and call + * @ref bindTextureTransformationBuffer() instead. */ PhongGL& setTextureMatrix(const Matrix3& matrix); @@ -774,6 +921,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @p positions array is the same as @ref lightCount(). Initial values * are @cpp {0.0f, 0.0f, 1.0f, 0.0f} @ce --- a directional "fill" light * coming from the camera. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref PhongLightUniform::position and call + * @ref bindLightBuffer() instead * @see @ref Shaders-PhongGL-lights, @ref setLightPosition() */ PhongGL& setLightPositions(Containers::ArrayView positions); @@ -813,6 +964,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * position. If updating more than one light, prefer the batch function * instead to reduce the count of GL API calls. Expects that @p id is * less than @ref lightCount(). + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref PhongLightUniform::position and call @ref bindLightBuffer() + * instead. */ PhongGL& setLightPosition(UnsignedInt id, const Vector4& position); @@ -844,6 +999,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * * Initial values are @cpp 0xffffff_rgbf @ce. Expects that the size * of the @p colors array is the same as @ref lightCount(). + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref PhongLightUniform::color and call @ref bindLightBuffer() + * instead. * @see @ref Shaders-PhongGL-lights, @ref setLightColor() */ PhongGL& setLightColors(Containers::ArrayView colors); @@ -879,6 +1038,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * updating more than one light, prefer the batch function instead to * reduce the count of GL API calls. Expects that @p id is less than * @ref lightCount(). + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref PhongLightUniform::color and call @ref bindLightBuffer() + * instead. */ PhongGL& setLightColor(UnsignedInt id, const Magnum::Color3& color); @@ -910,6 +1073,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * highlights on certain lights. Initial values are * @cpp 0xffffff_rgbf @ce. Expects that the size of the @p colors array * is the same as @ref lightCount(). + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref PhongLightUniform::specularColor and call @ref bindLightBuffer() + * instead. * @see @ref Shaders-PhongGL-lights, @ref setLightColor() */ PhongGL& setLightSpecularColors(Containers::ArrayView colors); @@ -929,6 +1096,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * color. If updating more than one light, prefer the batch function * instead to reduce the count of GL API calls. Expects that @p id is * less than @ref lightCount(). + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref PhongLightUniform::specularColor and call + * @ref bindLightBuffer() instead. */ PhongGL& setLightSpecularColor(UnsignedInt id, const Magnum::Color3& color); @@ -939,6 +1110,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * * Initial values are @ref Constants::inf(). Expects that the size of * the @p ranges array is the same as @ref lightCount(). + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref PhongLightUniform::range and call @ref bindLightBuffer() + * instead. * @see @ref Shaders-PhongGL-lights, @ref setLightRange() */ PhongGL& setLightRanges(Containers::ArrayView ranges); @@ -958,6 +1133,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * updating more than one light, prefer the batch function instead to * reduce the count of GL API calls. Expects that @p id is less than * @ref lightCount(). + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref PhongLightUniform::range and call @ref bindLightBuffer() + * instead. */ PhongGL& setLightRange(UnsignedInt id, Float range); @@ -965,6 +1144,158 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @} */ + #ifndef MAGNUM_TARGET_GLES2 + /** @{ + * @name Uniform buffer binding and related uniform setters + * + * Used if @ref Flag::UniformBuffers is set. + */ + + /** + * @brief Set a draw offset + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Specifies which item in the @ref TransformationUniform3D, + * @ref PhongDrawUniform and @ref TextureTransformationUniform buffers + * bound with @ref bindTransformationBuffer(), @ref bindDrawBuffer() + * and @ref bindTextureTransformationBuffer() should be used for + * current draw. Expects that @ref Flag::UniformBuffers is set and + * @p offset is less than @ref drawCount(). Initial value is @cpp 0 @ce. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + PhongGL& setDrawOffset(UnsignedInt offset); + + /** + * @brief Set a projection uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain at least one instance of + * @ref ProjectionUniform3D. At the very least you need to call also + * @ref bindTransformationBuffer(), @ref bindDrawBuffer() and + * @ref bindMaterialBuffer(), usually @ref bindLightBuffer() as well. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + PhongGL& bindProjectionBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + PhongGL& bindProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a transformation uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref drawCount() instances of + * @ref TransformationUniform3D. At the very least you need to call + * also @ref bindProjectionBuffer(), @ref bindDrawBuffer() and + * @ref bindMaterialBuffer(), usually @ref bindLightBuffer() as well. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + PhongGL& bindTransformationBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + PhongGL& bindTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a draw uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref drawCount() instances of + * @ref PhongDrawUniform. At the very least you need to call also + * @ref bindProjectionBuffer(), @ref bindTransformationBuffer() and + * @ref bindMaterialBuffer(), usually @ref bindLightBuffer() as well. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + PhongGL& bindDrawBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + PhongGL& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a texture transformation uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that both @ref Flag::UniformBuffers and + * @ref Flag::TextureTransformation is set. The buffer is expected to + * contain @ref drawCount() instances of + * @ref TextureTransformationUniform. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + PhongGL& bindTextureTransformationBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + PhongGL& bindTextureTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a material uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref materialCount() instances of + * @ref PhongMaterialUniform. At the very least you need to call also + * @ref bindProjectionBuffer(), @ref bindTransformationBuffer() and + * @ref bindDrawBuffer(), usually @ref bindLightBuffer() as well. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + PhongGL& bindMaterialBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + PhongGL& bindMaterialBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a light uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref lightCount() instances of + * @ref PhongLightUniform. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + PhongGL& bindLightBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + PhongGL& bindLightBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @} + */ + #endif + /** @{ * @name Texture binding */ @@ -1050,6 +1381,9 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { Flags _flags; UnsignedInt _lightCount{}; + #ifndef MAGNUM_TARGET_GLES2 + UnsignedInt _materialCount{}, _drawCount{}; + #endif Int _transformationMatrixUniform{0}, _projectionMatrixUniform{1}, _normalMatrixUniform{2}, @@ -1067,6 +1401,11 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { _lightColorsUniform, /* 11 + lightCount, set in the constructor */ _lightSpecularColorsUniform, /* 11 + 2*lightCount */ _lightRangesUniform; /* 11 + 3*lightCount */ + #ifndef MAGNUM_TARGET_GLES2 + /* Used instead of all other uniforms when Flag::UniformBuffers is set, + so it can alias them */ + Int _drawOffsetUniform{0}; + #endif }; /** @debugoperatorclassenum{PhongGL,PhongGL::Flag} */ diff --git a/src/Magnum/Shaders/Test/CMakeLists.txt b/src/Magnum/Shaders/Test/CMakeLists.txt index cbdaa1e40..c99df3824 100644 --- a/src/Magnum/Shaders/Test/CMakeLists.txt +++ b/src/Magnum/Shaders/Test/CMakeLists.txt @@ -23,6 +23,22 @@ # DEALINGS IN THE SOFTWARE. # +corrade_add_test(ShadersDistanceFieldVectorTest DistanceFieldVectorTest.cpp LIBRARIES MagnumShaders) +corrade_add_test(ShadersFlatTest FlatTest.cpp LIBRARIES MagnumShaders) +corrade_add_test(ShadersGenericTest GenericTest.cpp LIBRARIES MagnumShaders) +corrade_add_test(ShadersMeshVisualizerTest MeshVisualizerTest.cpp LIBRARIES MagnumShaders) +corrade_add_test(ShadersPhongTest PhongTest.cpp LIBRARIES MagnumShaders) +corrade_add_test(ShadersVectorTest VectorTest.cpp LIBRARIES MagnumShaders) + +set_target_properties( + ShadersDistanceFieldVectorTest + ShadersFlatTest + ShadersGenericTest + ShadersMeshVisualizerTest + ShadersPhongTest + ShadersVectorTest + PROPERTIES FOLDER "Magnum/Shaders/Test") + # There's an underscore between GL and Test to disambiguate from GLTest, which # is a common suffix used to mark tests that need a GL context. Ugly, I know. corrade_add_test(ShadersDistanceFieldVectorGL_Test DistanceFieldVectorGL_Test.cpp LIBRARIES MagnumShaders) @@ -94,7 +110,9 @@ if(BUILD_GL_TESTS) VectorTestFiles/smooth0.2-2D.tga VectorTestFiles/smooth0.2-3D.tga VectorTestFiles/outline2D.tga - VectorTestFiles/outline3D.tga) + VectorTestFiles/outline3D.tga + VectorTestFiles/multidraw2D-distancefield.tga + VectorTestFiles/multidraw3D-distancefield.tga) target_include_directories(ShadersDistanceFieldVectorGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) if(BUILD_PLUGINS_STATIC) if(WITH_ANYIMAGEIMPORTER) @@ -133,7 +151,11 @@ if(BUILD_GL_TESTS) FlatTestFiles/textured2D-alpha-mask0.5.tga FlatTestFiles/textured3D-alpha-mask0.5.tga FlatTestFiles/vertexColor2D.tga - FlatTestFiles/vertexColor3D.tga) + FlatTestFiles/vertexColor3D.tga + FlatTestFiles/multidraw2D.tga + FlatTestFiles/multidraw3D.tga + FlatTestFiles/multidraw-textured2D.tga + FlatTestFiles/multidraw-textured3D.tga) target_include_directories(ShadersFlatGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) if(BUILD_PLUGINS_STATIC) if(WITH_ANYIMAGEIMPORTER) @@ -189,7 +211,14 @@ if(BUILD_GL_TESTS) MeshVisualizerTestFiles/wireframe-wide2D.tga MeshVisualizerTestFiles/wireframe-wide3D.tga MeshVisualizerTestFiles/wireframe2D.tga - MeshVisualizerTestFiles/wireframe3D.tga) + MeshVisualizerTestFiles/wireframe3D.tga + MeshVisualizerTestFiles/multidraw-wireframe2D.tga + MeshVisualizerTestFiles/multidraw-wireframe3D.tga + MeshVisualizerTestFiles/multidraw-wireframe-tbn3D.tga + MeshVisualizerTestFiles/multidraw-wireframe-nogeo2D.tga + MeshVisualizerTestFiles/multidraw-wireframe-nogeo3D.tga + MeshVisualizerTestFiles/multidraw-vertexid2D.tga + MeshVisualizerTestFiles/multidraw-vertexid3D.tga) target_include_directories(ShadersMeshVisualizerGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) if(BUILD_PLUGINS_STATIC) if(WITH_ANYIMAGEIMPORTER) @@ -251,6 +280,8 @@ if(BUILD_GL_TESTS) PhongTestFiles/light-point-range1.5.tga PhongTestFiles/light-point-specular-color.tga PhongTestFiles/light-point.tga + PhongTestFiles/multidraw.tga + PhongTestFiles/multidraw-textured.tga # For zero lights test (equivalency to Flat3D) FlatTestFiles/textured3D-alpha-mask0.5.tga) @@ -280,7 +311,9 @@ if(BUILD_GL_TESTS) VectorTestFiles/defaults.tga VectorTestFiles/vector2D.tga - VectorTestFiles/vector3D.tga) + VectorTestFiles/vector3D.tga + VectorTestFiles/multidraw2D.tga + VectorTestFiles/multidraw3D.tga) target_include_directories(ShadersVectorGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) if(BUILD_PLUGINS_STATIC) if(WITH_ANYIMAGEIMPORTER) @@ -306,7 +339,9 @@ if(BUILD_GL_TESTS) FlatTestFiles/defaults.tga VertexColorTestFiles/vertexColor2D.tga - VertexColorTestFiles/vertexColor3D.tga) + VertexColorTestFiles/vertexColor3D.tga + VertexColorTestFiles/multidraw2D.tga + VertexColorTestFiles/multidraw3D.tga) target_include_directories(ShadersVertexColorGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) if(BUILD_PLUGINS_STATIC) if(WITH_ANYIMAGEIMPORTER) diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp index ba84c7d4a..fd92030f2 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp @@ -52,6 +52,22 @@ #include "Magnum/Trade/ImageData.h" #include "Magnum/Trade/MeshData.h" +#ifndef MAGNUM_TARGET_GLES2 +#include + +#include "Magnum/GL/Extensions.h" +#include "Magnum/GL/MeshView.h" +#include "Magnum/MeshTools/Concatenate.h" +#include "Magnum/MeshTools/GenerateIndices.h" +#include "Magnum/Primitives/Circle.h" +#include "Magnum/Primitives/Cone.h" +#include "Magnum/Primitives/Plane.h" +#include "Magnum/Primitives/Square.h" +#include "Magnum/Primitives/UVSphere.h" +#include "Magnum/Shaders/DistanceFieldVector.h" +#include "Magnum/Shaders/Generic.h" +#endif + #include "configure.h" namespace Magnum { namespace Shaders { namespace Test { namespace { @@ -60,17 +76,43 @@ struct DistanceFieldVectorGLTest: GL::OpenGLTester { explicit DistanceFieldVectorGLTest(); template void construct(); + #ifndef MAGNUM_TARGET_GLES2 + template void constructUniformBuffers(); + #endif + template void constructMove(); + #ifndef MAGNUM_TARGET_GLES2 + template void constructMoveUniformBuffers(); + #endif + #ifndef MAGNUM_TARGET_GLES2 + template void constructUniformBuffersInvalid(); + #endif + + #ifndef MAGNUM_TARGET_GLES2 + template void setUniformUniformBuffersEnabled(); + template void bindBufferUniformBuffersNotEnabled(); + #endif template void setTextureMatrixNotEnabled(); + #ifndef MAGNUM_TARGET_GLES2 + template void bindTextureTransformBufferNotEnabled(); + #endif + #ifndef MAGNUM_TARGET_GLES2 + template void setWrongDrawOffset(); + #endif void renderSetup(); void renderTeardown(); - void renderDefaults2D(); - void renderDefaults3D(); - void render2D(); - void render3D(); + template void renderDefaults2D(); + template void renderDefaults3D(); + template void render2D(); + template void render3D(); + + #ifndef MAGNUM_TARGET_GLES2 + void renderMulti2D(); + void renderMulti3D(); + #endif private: PluginManager::Manager _manager{"nonexistent"}; @@ -105,6 +147,31 @@ constexpr struct { {"texture transformation", DistanceFieldVectorGL2D::Flag::TextureTransformation} }; +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + DistanceFieldVectorGL2D::Flags flags; + UnsignedInt materialCount, drawCount; +} ConstructUniformBuffersData[]{ + {"classic fallback", {}, 1, 1}, + {"", DistanceFieldVectorGL2D::Flag::UniformBuffers, 1, 1}, + {"texture transformation", DistanceFieldVectorGL2D::Flag::UniformBuffers|DistanceFieldVectorGL2D::Flag::TextureTransformation, 1, 1}, + {"multiple materials, draws", DistanceFieldVectorGL2D::Flag::UniformBuffers, 64, 128}, +}; + +constexpr struct { + const char* name; + DistanceFieldVectorGL2D::Flags flags; + UnsignedInt materialCount, drawCount; + const char* message; +} ConstructUniformBuffersInvalidData[]{ + {"zero draws", DistanceFieldVectorGL2D::Flag::UniformBuffers, 1, 0, + "draw count can't be zero"}, + {"zero materials", DistanceFieldVectorGL2D::Flag::UniformBuffers, 0, 1, + "material count can't be zero"}, +}; +#endif + const struct { const char* name; DistanceFieldVectorGL2D::Flags flags; @@ -127,30 +194,106 @@ const struct { "outline2D.tga", "outline3D.tga", false} }; +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + const char* expected2D; + const char* expected3D; + UnsignedInt materialCount, drawCount; + UnsignedInt uniformIncrement; + Float maxThreshold, meanThreshold; +} RenderMultiData[] { + {"bind with offset", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", + 1, 1, 16, 0.0f, 0.0f}, + {"draw offset", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", + 2, 3, 1, 0.0f, 0.0f}, +}; +#endif + DistanceFieldVectorGLTest::DistanceFieldVectorGLTest() { addInstancedTests({ &DistanceFieldVectorGLTest::construct<2>, &DistanceFieldVectorGLTest::construct<3>}, Containers::arraySize(ConstructData)); + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({ + &DistanceFieldVectorGLTest::constructUniformBuffers<2>, + &DistanceFieldVectorGLTest::constructUniformBuffers<3>}, + Containers::arraySize(ConstructUniformBuffersData)); + #endif + addTests({ &DistanceFieldVectorGLTest::constructMove<2>, &DistanceFieldVectorGLTest::constructMove<3>, + #ifndef MAGNUM_TARGET_GLES2 + &DistanceFieldVectorGLTest::constructMoveUniformBuffers<2>, + &DistanceFieldVectorGLTest::constructMoveUniformBuffers<3>, + #endif + }); + + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({ + &DistanceFieldVectorGLTest::constructUniformBuffersInvalid<2>, + &DistanceFieldVectorGLTest::constructUniformBuffersInvalid<3>}, + Containers::arraySize(ConstructUniformBuffersInvalidData)); + #endif + + addTests({ + #ifndef MAGNUM_TARGET_GLES2 + &DistanceFieldVectorGLTest::setUniformUniformBuffersEnabled<2>, + &DistanceFieldVectorGLTest::setUniformUniformBuffersEnabled<3>, + &DistanceFieldVectorGLTest::bindBufferUniformBuffersNotEnabled<2>, + &DistanceFieldVectorGLTest::bindBufferUniformBuffersNotEnabled<3>, + #endif &DistanceFieldVectorGLTest::setTextureMatrixNotEnabled<2>, - &DistanceFieldVectorGLTest::setTextureMatrixNotEnabled<3>}); + &DistanceFieldVectorGLTest::setTextureMatrixNotEnabled<3>, + #ifndef MAGNUM_TARGET_GLES2 + &DistanceFieldVectorGLTest::bindTextureTransformBufferNotEnabled<2>, + &DistanceFieldVectorGLTest::bindTextureTransformBufferNotEnabled<3>, + #endif + #ifndef MAGNUM_TARGET_GLES2 + &DistanceFieldVectorGLTest::setWrongDrawOffset<2>, + &DistanceFieldVectorGLTest::setWrongDrawOffset<3> + #endif + }); - addTests({&DistanceFieldVectorGLTest::renderDefaults2D, - &DistanceFieldVectorGLTest::renderDefaults3D}, + addTests({ + &DistanceFieldVectorGLTest::renderDefaults2D, + #ifndef MAGNUM_TARGET_GLES2 + &DistanceFieldVectorGLTest::renderDefaults2D, + #endif + &DistanceFieldVectorGLTest::renderDefaults3D, + #ifndef MAGNUM_TARGET_GLES2 + &DistanceFieldVectorGLTest::renderDefaults3D, + #endif + }, &DistanceFieldVectorGLTest::renderSetup, &DistanceFieldVectorGLTest::renderTeardown); - addInstancedTests({&DistanceFieldVectorGLTest::render2D, - &DistanceFieldVectorGLTest::render3D}, + addInstancedTests({ + &DistanceFieldVectorGLTest::render2D, + #ifndef MAGNUM_TARGET_GLES2 + &DistanceFieldVectorGLTest::render2D, + #endif + &DistanceFieldVectorGLTest::render3D, + #ifndef MAGNUM_TARGET_GLES2 + &DistanceFieldVectorGLTest::render3D, + #endif + }, Containers::arraySize(RenderData), &DistanceFieldVectorGLTest::renderSetup, &DistanceFieldVectorGLTest::renderTeardown); + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({&DistanceFieldVectorGLTest::renderMulti2D, + &DistanceFieldVectorGLTest::renderMulti3D}, + Containers::arraySize(RenderMultiData), + &DistanceFieldVectorGLTest::renderSetup, + &DistanceFieldVectorGLTest::renderTeardown); + #endif + /* 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 @@ -194,6 +337,34 @@ template void DistanceFieldVectorGLTest::construct() { MAGNUM_VERIFY_NO_GL_ERROR(); } +#ifndef MAGNUM_TARGET_GLES2 +template void DistanceFieldVectorGLTest::constructUniformBuffers() { + setTestCaseTemplateName(std::to_string(dimensions)); + + auto&& data = ConstructUniformBuffersData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if((data.flags & DistanceFieldVectorGL2D::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + DistanceFieldVectorGL shader{data.flags, data.materialCount, data.drawCount}; + CORRADE_COMPARE(shader.flags(), data.flags); + CORRADE_COMPARE(shader.materialCount(), data.materialCount); + CORRADE_COMPARE(shader.drawCount(), data.drawCount); + CORRADE_VERIFY(shader.id()); + { + #ifdef CORRADE_TARGET_APPLE + CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); + #endif + CORRADE_VERIFY(shader.validate().first); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); +} +#endif + template void DistanceFieldVectorGLTest::constructMove() { setTestCaseTemplateName(std::to_string(dimensions)); @@ -215,6 +386,128 @@ template void DistanceFieldVectorGLTest::constructMove() CORRADE_VERIFY(!b.id()); } +#ifndef MAGNUM_TARGET_GLES2 +template void DistanceFieldVectorGLTest::constructMoveUniformBuffers() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + DistanceFieldVectorGL a{DistanceFieldVectorGL::Flag::UniformBuffers, 2, 5}; + const GLuint id = a.id(); + CORRADE_VERIFY(id); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + DistanceFieldVectorGL b{std::move(a)}; + CORRADE_COMPARE(b.id(), id); + CORRADE_COMPARE(b.flags(), DistanceFieldVectorGL::Flag::UniformBuffers); + CORRADE_COMPARE(b.materialCount(), 2); + CORRADE_COMPARE(b.drawCount(), 5); + CORRADE_VERIFY(!a.id()); + + DistanceFieldVectorGL c{NoCreate}; + c = std::move(b); + CORRADE_COMPARE(c.id(), id); + CORRADE_COMPARE(c.flags(), DistanceFieldVectorGL::Flag::UniformBuffers); + CORRADE_COMPARE(c.materialCount(), 2); + CORRADE_COMPARE(c.drawCount(), 5); + CORRADE_VERIFY(!b.id()); +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +template void DistanceFieldVectorGLTest::constructUniformBuffersInvalid() { + auto&& data = ConstructUniformBuffersInvalidData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + setTestCaseTemplateName(std::to_string(dimensions)); + + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + DistanceFieldVectorGL{data.flags, data.materialCount, data.drawCount}; + CORRADE_COMPARE(out.str(), Utility::formatString( + "Shaders::DistanceFieldVectorGL: {}\n", data.message)); +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +template void DistanceFieldVectorGLTest::setUniformUniformBuffersEnabled() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + DistanceFieldVectorGL shader{DistanceFieldVectorGL::Flag::UniformBuffers}; + shader.setTransformationProjectionMatrix({}) + .setTextureMatrix({}) + .setColor({}) + .setOutlineColor({}) + .setOutlineRange({}, {}) + .setSmoothness({}); + CORRADE_COMPARE(out.str(), + "Shaders::DistanceFieldVectorGL::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::DistanceFieldVectorGL::setTextureMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::DistanceFieldVectorGL::setColor(): the shader was created with uniform buffers enabled\n" + "Shaders::DistanceFieldVectorGL::setOutlineColor(): the shader was created with uniform buffers enabled\n" + "Shaders::DistanceFieldVectorGL::setOutlineRange(): the shader was created with uniform buffers enabled\n" + "Shaders::DistanceFieldVectorGL::setSmoothness(): the shader was created with uniform buffers enabled\n"); +} + +template void DistanceFieldVectorGLTest::bindBufferUniformBuffersNotEnabled() { + 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}; + + GL::Buffer buffer; + DistanceFieldVectorGL shader; + shader.bindTransformationProjectionBuffer(buffer) + .bindTransformationProjectionBuffer(buffer, 0, 16) + .bindDrawBuffer(buffer) + .bindDrawBuffer(buffer, 0, 16) + .bindTextureTransformationBuffer(buffer) + .bindTextureTransformationBuffer(buffer, 0, 16) + .bindMaterialBuffer(buffer) + .bindMaterialBuffer(buffer, 0, 16) + .setDrawOffset(0); + CORRADE_COMPARE(out.str(), + "Shaders::DistanceFieldVectorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::DistanceFieldVectorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::DistanceFieldVectorGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::DistanceFieldVectorGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::DistanceFieldVectorGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::DistanceFieldVectorGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::DistanceFieldVectorGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::DistanceFieldVectorGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::DistanceFieldVectorGL::setDrawOffset(): the shader was not created with uniform buffers enabled\n"); +} +#endif + template void DistanceFieldVectorGLTest::setTextureMatrixNotEnabled() { setTestCaseTemplateName(std::to_string(dimensions)); @@ -232,6 +525,54 @@ template void DistanceFieldVectorGLTest::setTextureMatri "Shaders::DistanceFieldVectorGL::setTextureMatrix(): the shader was not created with texture transformation enabled\n"); } +#ifndef MAGNUM_TARGET_GLES2 +template void DistanceFieldVectorGLTest::bindTextureTransformBufferNotEnabled() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + GL::Buffer buffer{GL::Buffer::TargetHint::Uniform}; + DistanceFieldVectorGL shader{DistanceFieldVectorGL::Flag::UniformBuffers}; + shader.bindTextureTransformationBuffer(buffer) + .bindTextureTransformationBuffer(buffer, 0, 16); + CORRADE_COMPARE(out.str(), + "Shaders::DistanceFieldVectorGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled\n" + "Shaders::DistanceFieldVectorGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled\n"); +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +template void DistanceFieldVectorGLTest::setWrongDrawOffset() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + DistanceFieldVectorGL{DistanceFieldVectorGL::Flag::UniformBuffers, 2, 5} + .setDrawOffset(5); + CORRADE_COMPARE(out.str(), + "Shaders::DistanceFieldVectorGL::setDrawOffset(): draw offset 5 is out of bounds for 5 draws\n"); +} +#endif + constexpr Vector2i RenderSize{80, 80}; void DistanceFieldVectorGLTest::renderSetup() { @@ -267,7 +608,18 @@ constexpr GL::TextureFormat TextureFormatR = #endif ; -void DistanceFieldVectorGLTest::renderDefaults2D() { +template void DistanceFieldVectorGLTest::renderDefaults2D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == DistanceFieldVectorGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -293,9 +645,30 @@ void DistanceFieldVectorGLTest::renderDefaults2D() { .setSubImage(0, {}, *image); #endif - DistanceFieldVectorGL2D{} - .bindVectorTexture(texture) - .draw(square); + DistanceFieldVectorGL2D shader{flag}; + shader.bindVectorTexture(texture); + + if(flag == DistanceFieldVectorGL2D::Flag{}) { + shader.draw(square); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == DistanceFieldVectorGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + DistanceFieldVectorDrawUniform{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + DistanceFieldVectorMaterialUniform{} + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(square); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -323,7 +696,18 @@ void DistanceFieldVectorGLTest::renderDefaults2D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void DistanceFieldVectorGLTest::renderDefaults3D() { +template void DistanceFieldVectorGLTest::renderDefaults3D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == DistanceFieldVectorGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -349,9 +733,30 @@ void DistanceFieldVectorGLTest::renderDefaults3D() { .setSubImage(0, {}, *image); #endif - DistanceFieldVectorGL3D{} - .bindVectorTexture(texture) - .draw(plane); + DistanceFieldVectorGL3D shader{flag}; + shader.bindVectorTexture(texture); + + if(flag == DistanceFieldVectorGL3D::Flag{}) { + shader.draw(plane); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == DistanceFieldVectorGL3D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform3D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + DistanceFieldVectorDrawUniform{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + DistanceFieldVectorMaterialUniform{} + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(plane); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -379,10 +784,21 @@ void DistanceFieldVectorGLTest::renderDefaults3D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void DistanceFieldVectorGLTest::render2D() { +template void DistanceFieldVectorGLTest::render2D() { auto&& data = RenderData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == DistanceFieldVectorGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -408,21 +824,52 @@ void DistanceFieldVectorGLTest::render2D() { .setSubImage(0, {}, *image); #endif - DistanceFieldVectorGL2D shader{data.flags}; - shader - /** @todo implement background color */ - .setColor(data.color) - .setOutlineColor(data.outlineColor) - .setOutlineRange(data.outlineRangeStart, data.outlineRangeEnd) - .setSmoothness(data.smoothness) - .bindVectorTexture(texture); - - if(data.textureTransformation != Matrix3{}) - shader.setTextureMatrix(data.textureTransformation); - else shader.setTransformationProjectionMatrix( - Matrix3::projection({2.1f, 2.1f})); - - shader.draw(square); + DistanceFieldVectorGL2D shader{data.flags|flag}; + shader.bindVectorTexture(texture); + + if(flag == DistanceFieldVectorGL2D::Flag{}) { + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); + else shader.setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})); + shader.setColor(data.color) + .setOutlineColor(data.outlineColor) + .setOutlineRange(data.outlineRangeStart, data.outlineRangeEnd) + .setSmoothness(data.smoothness) + .draw(square); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == DistanceFieldVectorGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + data.textureTransformation == Matrix3{} ? + Matrix3::projection({2.1f, 2.1f}) : Matrix3{} + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + DistanceFieldVectorDrawUniform{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + DistanceFieldVectorMaterialUniform{} + .setColor(data.color) + .setOutlineColor(data.outlineColor) + .setOutlineRange(data.outlineRangeStart, data.outlineRangeEnd) + .setSmoothness(data.smoothness) + }}; + GL::Buffer textureTransformationlUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setTextureMatrix(data.textureTransformation) + }}; + if(data.flags & DistanceFieldVectorGL2D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationlUniform); + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(square); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -445,10 +892,21 @@ void DistanceFieldVectorGLTest::render2D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void DistanceFieldVectorGLTest::render3D() { +template void DistanceFieldVectorGLTest::render3D() { auto&& data = RenderData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == DistanceFieldVectorGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -474,24 +932,58 @@ void DistanceFieldVectorGLTest::render3D() { .setSubImage(0, {}, *image); #endif - DistanceFieldVectorGL3D shader{data.flags}; - shader - /** @todo implement background color */ - .setColor(data.color) - .setOutlineColor(data.outlineColor) - .setOutlineRange(data.outlineRangeStart, data.outlineRangeEnd) - .setSmoothness(data.smoothness) - .bindVectorTexture(texture); - - if(data.textureTransformation != Matrix3{}) - shader.setTextureMatrix(data.textureTransformation); - else shader.setTransformationProjectionMatrix( - Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* - Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationZ(15.0_degf)); - - shader.draw(plane); + DistanceFieldVectorGL3D shader{data.flags|flag}; + shader.bindVectorTexture(texture); + + if(flag == DistanceFieldVectorGL3D::Flag{}) { + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); + else shader.setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationZ(15.0_degf)); + shader.setColor(data.color) + .setOutlineColor(data.outlineColor) + .setOutlineRange(data.outlineRangeStart, data.outlineRangeEnd) + .setSmoothness(data.smoothness) + .draw(plane); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == DistanceFieldVectorGL3D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + data.textureTransformation == Matrix3{} ? + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationZ(15.0_degf) : Matrix4{} + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + DistanceFieldVectorDrawUniform{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + DistanceFieldVectorMaterialUniform{} + .setColor(data.color) + .setOutlineColor(data.outlineColor) + .setOutlineRange(data.outlineRangeStart, data.outlineRangeEnd) + .setSmoothness(data.smoothness) + }}; + GL::Buffer textureTransformationlUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setTextureMatrix(data.textureTransformation) + }}; + if(data.flags & DistanceFieldVectorGL2D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationlUniform); + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(plane); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -514,6 +1006,369 @@ void DistanceFieldVectorGLTest::render3D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } +#ifndef MAGNUM_TARGET_GLES2 +void DistanceFieldVectorGLTest::renderMulti2D() { + auto&& data = RenderMultiData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::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."); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + Containers::Optional image; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/vector-distancefield.tga")) && (image = importer->image2D(0))); + GL::Texture2D vector; + vector.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R8, image->size()) + .setSubImage(0, {}, *image); + + /* Circle is a fan, plane is a strip, make it indexed first */ + Trade::MeshData circleData = MeshTools::generateIndices(Primitives::circle2DSolid(32, + Primitives::Circle2DFlag::TextureCoordinates)); + Trade::MeshData squareData = MeshTools::generateIndices(Primitives::squareSolid( + Primitives::SquareFlag::TextureCoordinates)); + Trade::MeshData triangleData = MeshTools::generateIndices(Primitives::circle2DSolid(3, + Primitives::Circle2DFlag::TextureCoordinates)); + GL::Mesh mesh = MeshTools::compile(MeshTools::concatenate({circleData, squareData, triangleData})); + GL::MeshView circle{mesh}; + circle.setCount(circleData.indexCount()); + GL::MeshView square{mesh}; + square.setCount(squareData.indexCount()) + .setIndexRange(circleData.indexCount()); + GL::MeshView triangle{mesh}; + triangle.setCount(triangleData.indexCount()) + .setIndexRange(circleData.indexCount() + squareData.indexCount()); + + /* Some drivers have uniform offset alignment as high as 256, which means + the subsequent sets of uniforms have to be aligned to a multiply of it. + The data.uniformIncrement is set high enough to ensure that, in the + non-offset-bind case this value is 1. */ + + Containers::Array materialData{data.uniformIncrement + 1}; + materialData[0*data.uniformIncrement] = DistanceFieldVectorMaterialUniform{} + .setColor(0x00ff00_rgbf); + materialData[1*data.uniformIncrement] = DistanceFieldVectorMaterialUniform{} + .setColor(0x990000_rgbf) + .setOutlineColor(0xff0000_rgbf) + .setOutlineRange(0.6f, 0.4f); + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, materialData}; + + Containers::Array transformationProjectionData{2*data.uniformIncrement + 1}; + transformationProjectionData[0*data.uniformIncrement] = TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})* + Matrix3::translation({-1.25f, -1.25f}) + ); + transformationProjectionData[1*data.uniformIncrement] = TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})* + Matrix3::translation({ 1.25f, -1.25f}) + ); + transformationProjectionData[2*data.uniformIncrement] = TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})* + Matrix3::translation({ 0.00f, 1.25f}) + ); + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, transformationProjectionData}; + + Containers::Array textureTransformationData{2*data.uniformIncrement + 1}; + textureTransformationData[0*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::translation({0.5f, 0.5f})* + Matrix3::rotation(180.0_degf)* + Matrix3::translation({-0.5f, -0.5f}) + ); + textureTransformationData[1*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::translation(Vector2::xAxis(1.0f))* + Matrix3::scaling(Vector2::xScale(-1.0f)) + ); + textureTransformationData[2*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix(Matrix3{}); + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; + + Containers::Array drawData{2*data.uniformIncrement + 1}; + /* Material offsets are zero if we have single draw, as those are done with + UBO offset bindings instead. */ + drawData[0*data.uniformIncrement] = DistanceFieldVectorDrawUniform{} + .setMaterialId(data.drawCount == 1 ? 0 : 0); + drawData[1*data.uniformIncrement] = DistanceFieldVectorDrawUniform{} + .setMaterialId(data.drawCount == 1 ? 0 : 1); + drawData[2*data.uniformIncrement] = DistanceFieldVectorDrawUniform{} + .setMaterialId(data.drawCount == 1 ? 0 : 0); + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; + + DistanceFieldVectorGL2D shader{DistanceFieldVectorGL2D::Flag::UniformBuffers|DistanceFieldVectorGL2D::Flag::TextureTransformation, data.materialCount, data.drawCount}; + shader.bindVectorTexture(vector); + + /* Just one draw, rebinding UBOs each time */ + if(data.drawCount == 1) { + shader.bindMaterialBuffer(materialUniform, + 0*data.uniformIncrement*sizeof(DistanceFieldVectorMaterialUniform), + sizeof(DistanceFieldVectorMaterialUniform)); + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 0*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), + sizeof(TransformationProjectionUniform2D)); + shader.bindDrawBuffer(drawUniform, + 0*data.uniformIncrement*sizeof(DistanceFieldVectorDrawUniform), + sizeof(DistanceFieldVectorDrawUniform)); + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 0*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(circle); + + shader.bindMaterialBuffer(materialUniform, + 1*data.uniformIncrement*sizeof(DistanceFieldVectorMaterialUniform), + sizeof(DistanceFieldVectorMaterialUniform)); + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 1*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), + sizeof(TransformationProjectionUniform2D)); + shader.bindDrawBuffer(drawUniform, + 1*data.uniformIncrement*sizeof(DistanceFieldVectorDrawUniform), + sizeof(DistanceFieldVectorDrawUniform)); + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 1*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(square); + + shader.bindMaterialBuffer(materialUniform, + 0*data.uniformIncrement*sizeof(DistanceFieldVectorMaterialUniform), + sizeof(DistanceFieldVectorMaterialUniform)); + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 2*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), + sizeof(TransformationProjectionUniform2D)); + shader.bindDrawBuffer(drawUniform, + 2*data.uniformIncrement*sizeof(DistanceFieldVectorDrawUniform), + sizeof(DistanceFieldVectorDrawUniform)); + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 2*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(triangle); + + /* Otherwise using the draw offset */ + } else { + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .bindTextureTransformationBuffer(textureTransformationUniform); + shader.setDrawOffset(0) + .draw(circle); + shader.setDrawOffset(1) + .draw(square); + shader.setDrawOffset(2) + .draw(triangle); + } + + /* + - Circle lower left, green, upside down + - Square lower right, dark red with red outline, mirrored + - Triangle up center, green + */ + MAGNUM_VERIFY_NO_GL_ERROR(); + 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, "VectorTestFiles", data.expected2D}), + (DebugTools::CompareImageToFile{_manager, data.maxThreshold, data.meanThreshold})); +} + +void DistanceFieldVectorGLTest::renderMulti3D() { + auto&& data = RenderMultiData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::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."); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + Containers::Optional image; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/vector-distancefield.tga")) && (image = importer->image2D(0))); + GL::Texture2D vector; + vector.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R8, image->size()) + .setSubImage(0, {}, *image); + + Trade::MeshData sphereData = Primitives::uvSphereSolid(16, 32, + Primitives::UVSphereFlag::TextureCoordinates); + /* Plane is a strip, make it indexed first */ + Trade::MeshData planeData = MeshTools::generateIndices(Primitives::planeSolid( + Primitives::PlaneFlag::TextureCoordinates)); + Trade::MeshData coneData = Primitives::coneSolid(1, 32, 1.0f, + Primitives::ConeFlag::TextureCoordinates); + GL::Mesh mesh = MeshTools::compile(MeshTools::concatenate({sphereData, planeData, coneData})); + GL::MeshView sphere{mesh}; + sphere.setCount(sphereData.indexCount()); + GL::MeshView plane{mesh}; + plane.setCount(planeData.indexCount()) + .setIndexRange(sphereData.indexCount()); + GL::MeshView cone{mesh}; + cone.setCount(coneData.indexCount()) + .setIndexRange(sphereData.indexCount() + planeData.indexCount()); + + /* Some drivers have uniform offset alignment as high as 256, which means + the subsequent sets of uniforms have to be aligned to a multiply of it. + The data.uniformIncrement is set high enough to ensure that, in the + non-offset-bind case this value is 1. */ + + Containers::Array materialData{data.uniformIncrement + 1}; + materialData[0*data.uniformIncrement] = DistanceFieldVectorMaterialUniform{} + .setColor(0x00ff00_rgbf); + materialData[1*data.uniformIncrement] = DistanceFieldVectorMaterialUniform{} + .setColor(0x990000_rgbf) + .setOutlineColor(0xff0000_rgbf) + .setOutlineRange(0.6f, 0.4f); + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, materialData}; + + Containers::Array transformationProjectionData{2*data.uniformIncrement + 1}; + transformationProjectionData[0*data.uniformIncrement] = TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({-1.25f, -1.25f, 0.0f})* + Matrix4::rotationY(180.0_degf) /* so the texture is visible */ + ); + transformationProjectionData[1*data.uniformIncrement] = TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({ 1.25f, -1.25f, 0.0f}) + ); + transformationProjectionData[2*data.uniformIncrement] = TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({ 0.0f, 1.0f, 1.0f})* + Matrix4::rotationY(180.0_degf) /* so the texture is visible */ + ); + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, transformationProjectionData}; + + Containers::Array textureTransformationData{2*data.uniformIncrement + 1}; + textureTransformationData[0*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::translation({0.5f, 0.5f})* + Matrix3::rotation(180.0_degf)* + Matrix3::translation({-0.5f, -0.5f}) + ); + textureTransformationData[1*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::translation(Vector2::xAxis(1.0f))* + Matrix3::scaling(Vector2::xScale(-1.0f)) + ); + textureTransformationData[2*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix(Matrix3{}); + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; + + Containers::Array drawData{2*data.uniformIncrement + 1}; + /* Material offsets are zero if we have single draw, as those are done with + UBO offset bindings instead. */ + drawData[0*data.uniformIncrement] = DistanceFieldVectorDrawUniform{} + .setMaterialId(data.drawCount == 1 ? 0 : 0); + drawData[1*data.uniformIncrement] = DistanceFieldVectorDrawUniform{} + .setMaterialId(data.drawCount == 1 ? 0 : 1); + drawData[2*data.uniformIncrement] = DistanceFieldVectorDrawUniform{} + .setMaterialId(data.drawCount == 1 ? 0 : 0); + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; + + DistanceFieldVectorGL3D shader{DistanceFieldVectorGL3D::Flag::UniformBuffers|DistanceFieldVectorGL3D::Flag::TextureTransformation, data.materialCount, data.drawCount}; + shader.bindVectorTexture(vector); + + /* Just one draw, rebinding UBOs each time */ + if(data.drawCount == 1) { + shader.bindMaterialBuffer(materialUniform, + 0*data.uniformIncrement*sizeof(DistanceFieldVectorMaterialUniform), + sizeof(DistanceFieldVectorMaterialUniform)); + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 0*data.uniformIncrement*sizeof(TransformationProjectionUniform3D), + sizeof(TransformationProjectionUniform3D)); + shader.bindDrawBuffer(drawUniform, + 0*data.uniformIncrement*sizeof(DistanceFieldVectorDrawUniform), + sizeof(DistanceFieldVectorDrawUniform)); + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 0*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(sphere); + + shader.bindMaterialBuffer(materialUniform, + 1*data.uniformIncrement*sizeof(DistanceFieldVectorMaterialUniform), + sizeof(DistanceFieldVectorMaterialUniform)); + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 1*data.uniformIncrement*sizeof(TransformationUniform3D), + sizeof(TransformationUniform3D)); + shader.bindDrawBuffer(drawUniform, + 1*data.uniformIncrement*sizeof(DistanceFieldVectorDrawUniform), + sizeof(DistanceFieldVectorDrawUniform)); + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 1*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(plane); + + shader.bindMaterialBuffer(materialUniform, + 0*data.uniformIncrement*sizeof(DistanceFieldVectorMaterialUniform), + sizeof(DistanceFieldVectorMaterialUniform)); + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 2*data.uniformIncrement*sizeof(TransformationUniform3D), + sizeof(TransformationUniform3D)); + shader.bindDrawBuffer(drawUniform, + 2*data.uniformIncrement*sizeof(DistanceFieldVectorDrawUniform), + sizeof(DistanceFieldVectorDrawUniform)); + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 2*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(cone); + + /* Otherwise using the draw offset */ + } else { + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .bindTextureTransformationBuffer(textureTransformationUniform); + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } + + /* + - Sphere lower left, green, upside down + - Plane lower right, dark red with red outline, mirrored + - Cone up center, green + */ + MAGNUM_VERIFY_NO_GL_ERROR(); + 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, "VectorTestFiles", data.expected3D}), + (DebugTools::CompareImageToFile{_manager, data.maxThreshold, data.meanThreshold})); +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::DistanceFieldVectorGLTest) diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorTest.cpp new file mode 100644 index 000000000..bdb22c184 --- /dev/null +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorTest.cpp @@ -0,0 +1,195 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include + +#include "Magnum/Shaders/DistanceFieldVector.h" + +namespace Magnum { namespace Shaders { namespace Test { namespace { + +struct DistanceFieldVectorTest: TestSuite::Tester { + explicit DistanceFieldVectorTest(); + + template void uniformSize(); + + void drawUniformConstructDefault(); + void drawUniformConstructNoInit(); + void drawUniformSetters(); + void drawUniformMaterialIdPacking(); + + void materialUniformConstructDefault(); + void materialUniformConstructNoInit(); + void materialUniformSetters(); +}; + +DistanceFieldVectorTest::DistanceFieldVectorTest() { + addTests({&DistanceFieldVectorTest::uniformSize, + &DistanceFieldVectorTest::uniformSize, + + &DistanceFieldVectorTest::drawUniformConstructDefault, + &DistanceFieldVectorTest::drawUniformConstructNoInit, + &DistanceFieldVectorTest::drawUniformSetters, + &DistanceFieldVectorTest::drawUniformMaterialIdPacking, + + &DistanceFieldVectorTest::materialUniformConstructDefault, + &DistanceFieldVectorTest::materialUniformConstructNoInit, + &DistanceFieldVectorTest::materialUniformSetters}); +} + +using namespace Math::Literals; + +template struct UniformTraits; +template<> struct UniformTraits { + static const char* name() { return "DistanceFieldVectorDrawUniform"; } +}; +template<> struct UniformTraits { + static const char* name() { return "DistanceFieldVectorMaterialUniform"; } +}; + +template void DistanceFieldVectorTest::uniformSize() { + setTestCaseTemplateName(UniformTraits::name()); + + CORRADE_FAIL_IF(sizeof(T) % sizeof(Vector4) != 0, sizeof(T) << "is not a multiple of vec4 for UBO alignment."); + + /* 48-byte structures are fine, we'll align them to 768 bytes and not + 256, but warn about that */ + CORRADE_FAIL_IF(768 % sizeof(T) != 0, sizeof(T) << "can't fit exactly into 768-byte UBO alignment."); + if(256 % sizeof(T) != 0) + CORRADE_WARN(sizeof(T) << "can't fit exactly into 256-byte UBO alignment, only 768."); +} + +void DistanceFieldVectorTest::drawUniformConstructDefault() { + DistanceFieldVectorDrawUniform a; + DistanceFieldVectorDrawUniform b{DefaultInit}; + CORRADE_COMPARE(a.materialId, 0); + CORRADE_COMPARE(b.materialId, 0); + + constexpr DistanceFieldVectorDrawUniform ca; + constexpr DistanceFieldVectorDrawUniform cb{DefaultInit}; + CORRADE_COMPARE(ca.materialId, 0); + CORRADE_COMPARE(cb.materialId, 0); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void DistanceFieldVectorTest::drawUniformConstructNoInit() { + /* Testing only some fields, should be enough */ + DistanceFieldVectorDrawUniform a; + a.materialId = 76; + + new(&a) DistanceFieldVectorDrawUniform{NoInit}; + CORRADE_COMPARE(a.materialId, 76); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void DistanceFieldVectorTest::drawUniformSetters() { + DistanceFieldVectorDrawUniform a; + a.setMaterialId(76); + CORRADE_COMPARE(a.materialId, 76); +} + +void DistanceFieldVectorTest::drawUniformMaterialIdPacking() { + DistanceFieldVectorDrawUniform a; + a.setMaterialId(13765); + /* materialId should be right at the beginning, in the low 16 bits on both + LE and BE */ + CORRADE_COMPARE(reinterpret_cast(&a)[0] & 0xffff, 13765); +} + +void DistanceFieldVectorTest::materialUniformConstructDefault() { + DistanceFieldVectorMaterialUniform a; + DistanceFieldVectorMaterialUniform b{DefaultInit}; + CORRADE_COMPARE(a.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(b.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(a.outlineColor, 0x00000000_rgbaf); + CORRADE_COMPARE(b.outlineColor, 0x00000000_rgbaf); + CORRADE_COMPARE(a.outlineStart, 0.5f); + CORRADE_COMPARE(b.outlineStart, 0.5f); + CORRADE_COMPARE(a.outlineEnd, 1.0f); + CORRADE_COMPARE(b.outlineEnd, 1.0f); + CORRADE_COMPARE(a.smoothness, 0.04f); + CORRADE_COMPARE(b.smoothness, 0.04f); + + constexpr DistanceFieldVectorMaterialUniform ca; + constexpr DistanceFieldVectorMaterialUniform cb{DefaultInit}; + CORRADE_COMPARE(ca.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(cb.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(ca.outlineColor, 0x00000000_rgbaf); + CORRADE_COMPARE(cb.outlineColor, 0x00000000_rgbaf); + CORRADE_COMPARE(ca.outlineStart, 0.5f); + CORRADE_COMPARE(cb.outlineStart, 0.5f); + CORRADE_COMPARE(ca.outlineEnd, 1.0f); + CORRADE_COMPARE(cb.outlineEnd, 1.0f); + CORRADE_COMPARE(ca.smoothness, 0.04f); + CORRADE_COMPARE(cb.smoothness, 0.04f); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void DistanceFieldVectorTest::materialUniformConstructNoInit() { + /* Testing only some fields, should be enough */ + DistanceFieldVectorMaterialUniform a; + a.color = 0x354565fc_rgbaf; + a.outlineEnd = 0.37f; + + new(&a) DistanceFieldVectorMaterialUniform{NoInit}; + CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); + CORRADE_COMPARE(a.outlineEnd, 0.37f); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void DistanceFieldVectorTest::materialUniformSetters() { + DistanceFieldVectorMaterialUniform a; + a.setColor(0x354565fc_rgbaf) + .setOutlineColor(0x9876facd_rgbaf) + .setOutlineRange(0.6f, 0.1f) + .setSmoothness(0.37f); + CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); + CORRADE_COMPARE(a.outlineColor, 0x9876facd_rgbaf); + CORRADE_COMPARE(a.outlineStart, 0.6f); + CORRADE_COMPARE(a.outlineEnd, 0.1f); + CORRADE_COMPARE(a.smoothness, 0.37f); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Shaders::Test::DistanceFieldVectorTest) diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index e6bfa3207..35882e81c 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -56,6 +56,17 @@ #include "Magnum/Trade/ImageData.h" #include "Magnum/Trade/MeshData.h" +#ifndef MAGNUM_TARGET_GLES2 +#include "Magnum/GL/MeshView.h" +#include "Magnum/MeshTools/Concatenate.h" +#include "Magnum/MeshTools/GenerateIndices.h" +#include "Magnum/Primitives/Cone.h" +#include "Magnum/Primitives/Plane.h" +#include "Magnum/Primitives/Square.h" +#include "Magnum/Shaders/Generic.h" +#include "Magnum/Shaders/Flat.h" +#endif + #include "configure.h" namespace Magnum { namespace Shaders { namespace Test { namespace { @@ -64,49 +75,73 @@ struct FlatGLTest: GL::OpenGLTester { explicit FlatGLTest(); template void construct(); + #ifndef MAGNUM_TARGET_GLES2 + template void constructUniformBuffers(); + #endif template void constructMove(); + #ifndef MAGNUM_TARGET_GLES2 + template void constructMoveUniformBuffers(); + #endif template void constructTextureTransformationNotTextured(); + #ifndef MAGNUM_TARGET_GLES2 + template void constructUniformBuffersZeroDraws(); + #endif + #ifndef MAGNUM_TARGET_GLES2 + template void setUniformUniformBuffersEnabled(); + template void bindBufferUniformBuffersNotEnabled(); + #endif template void bindTextureNotEnabled(); template void setAlphaMaskNotEnabled(); template void setTextureMatrixNotEnabled(); #ifndef MAGNUM_TARGET_GLES2 + template void bindTextureTransformBufferNotEnabled(); + #endif + #ifndef MAGNUM_TARGET_GLES2 template void setObjectIdNotEnabled(); #endif + #ifndef MAGNUM_TARGET_GLES2 + template void setWrongDrawOffset(); + #endif void renderSetup(); void renderTeardown(); - void renderDefaults2D(); - void renderDefaults3D(); - void renderColored2D(); - void renderColored3D(); - void renderSinglePixelTextured2D(); - void renderSinglePixelTextured3D(); - void renderTextured2D(); - void renderTextured3D(); + template void renderDefaults2D(); + template void renderDefaults3D(); + template void renderColored2D(); + template void renderColored3D(); + template void renderSinglePixelTextured2D(); + template void renderSinglePixelTextured3D(); + template void renderTextured2D(); + template void renderTextured3D(); - template void renderVertexColor2D(); - template void renderVertexColor3D(); + template void renderVertexColor2D(); + template void renderVertexColor3D(); void renderAlphaSetup(); void renderAlphaTeardown(); - void renderAlpha2D(); - void renderAlpha3D(); + template void renderAlpha2D(); + template void renderAlpha3D(); #ifndef MAGNUM_TARGET_GLES2 void renderObjectIdSetup(); void renderObjectIdTeardown(); - void renderObjectId2D(); - void renderObjectId3D(); + template void renderObjectId2D(); + template void renderObjectId3D(); #endif - void renderInstanced2D(); - void renderInstanced3D(); + template void renderInstanced2D(); + template void renderInstanced3D(); + + #ifndef MAGNUM_TARGET_GLES2 + void renderMulti2D(); + void renderMulti3D(); + #endif private: PluginManager::Manager _manager{"nonexistent"}; @@ -156,6 +191,21 @@ constexpr struct { {"instanced texture offset", FlatGL2D::Flag::Textured|FlatGL2D::Flag::InstancedTextureOffset} }; +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + FlatGL2D::Flags flags; + UnsignedInt drawCount; +} ConstructUniformBuffersData[]{ + {"classic fallback", {}, 1}, + {"", FlatGL2D::Flag::UniformBuffers, 1}, + {"multiple draws", FlatGL2D::Flag::UniformBuffers, 128}, + {"texture transformation", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1}, + {"alpha mask", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::AlphaMask, 1}, + {"object ID", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::ObjectId, 1} +}; +#endif + const struct { const char* name; FlatGL2D::Flags flags; @@ -208,19 +258,64 @@ constexpr struct { }; #endif +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + const char* expected2D; + const char* expected3D; + FlatGL2D::Flags flags; + UnsignedInt drawCount; + UnsignedInt uniformIncrement; + Float maxThreshold, meanThreshold; +} RenderMultiData[] { + {"bind with offset, colored", "multidraw2D.tga", "multidraw3D.tga", + {}, 1, 16, 0.0f, 0.0f}, + {"bind with offset, textured", "multidraw-textured2D.tga", "multidraw-textured3D.tga", + FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured, + 1, 16, 0.0f, 0.0f}, + {"draw offset, colored", "multidraw2D.tga", "multidraw3D.tga", + {}, 3, 1, 0.0f, 0.0f}, + {"draw offset, textured", "multidraw-textured2D.tga", "multidraw-textured3D.tga", + FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured, + 3, 1, 0.0f, 0.0f} +}; +#endif + FlatGLTest::FlatGLTest() { addInstancedTests({ &FlatGLTest::construct<2>, &FlatGLTest::construct<3>}, Containers::arraySize(ConstructData)); + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({ + &FlatGLTest::constructUniformBuffers<2>, + &FlatGLTest::constructUniformBuffers<3>}, + Containers::arraySize(ConstructUniformBuffersData)); + #endif + addTests({ &FlatGLTest::constructMove<2>, &FlatGLTest::constructMove<3>, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::constructMoveUniformBuffers<2>, + &FlatGLTest::constructMoveUniformBuffers<3>, + #endif + &FlatGLTest::constructTextureTransformationNotTextured<2>, &FlatGLTest::constructTextureTransformationNotTextured<3>, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::constructUniformBuffersZeroDraws<2>, + &FlatGLTest::constructUniformBuffersZeroDraws<3>, + #endif + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::setUniformUniformBuffersEnabled<2>, + &FlatGLTest::setUniformUniformBuffersEnabled<3>, + &FlatGLTest::bindBufferUniformBuffersNotEnabled<2>, + &FlatGLTest::bindBufferUniformBuffersNotEnabled<3>, + #endif &FlatGLTest::bindTextureNotEnabled<2>, &FlatGLTest::bindTextureNotEnabled<3>, &FlatGLTest::setAlphaMaskNotEnabled<2>, @@ -228,52 +323,129 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::setTextureMatrixNotEnabled<2>, &FlatGLTest::setTextureMatrixNotEnabled<3>, #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::bindTextureTransformBufferNotEnabled<2>, + &FlatGLTest::bindTextureTransformBufferNotEnabled<3>, + #endif + #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::setObjectIdNotEnabled<2>, - &FlatGLTest::setObjectIdNotEnabled<3> + &FlatGLTest::setObjectIdNotEnabled<3>, #endif - }); - - addTests({&FlatGLTest::renderDefaults2D, - &FlatGLTest::renderDefaults3D, - &FlatGLTest::renderColored2D, - &FlatGLTest::renderColored3D, - &FlatGLTest::renderSinglePixelTextured2D, - &FlatGLTest::renderSinglePixelTextured3D}, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::setWrongDrawOffset<2>, + &FlatGLTest::setWrongDrawOffset<3>, + #endif + }); + + addTests({ + &FlatGLTest::renderDefaults2D, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderDefaults2D, + #endif + &FlatGLTest::renderDefaults3D, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderDefaults3D, + #endif + &FlatGLTest::renderColored2D, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderColored2D, + #endif + &FlatGLTest::renderColored3D, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderColored3D, + #endif + &FlatGLTest::renderSinglePixelTextured2D, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderSinglePixelTextured2D, + #endif + &FlatGLTest::renderSinglePixelTextured3D, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderSinglePixelTextured3D + #endif + }, &FlatGLTest::renderSetup, &FlatGLTest::renderTeardown); - addInstancedTests({&FlatGLTest::renderTextured2D, - &FlatGLTest::renderTextured3D}, + addInstancedTests({ + &FlatGLTest::renderTextured2D, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderTextured2D, + #endif + &FlatGLTest::renderTextured3D, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderTextured3D + #endif + }, Containers::arraySize(RenderTexturedData), &FlatGLTest::renderSetup, &FlatGLTest::renderTeardown); - addTests({&FlatGLTest::renderVertexColor2D, - &FlatGLTest::renderVertexColor2D, - &FlatGLTest::renderVertexColor3D, - &FlatGLTest::renderVertexColor3D}, + addTests({ + &FlatGLTest::renderVertexColor2D, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderVertexColor2D, + #endif + &FlatGLTest::renderVertexColor2D, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderVertexColor2D, + #endif + &FlatGLTest::renderVertexColor3D, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderVertexColor3D, + #endif + &FlatGLTest::renderVertexColor3D, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderVertexColor3D + #endif + }, &FlatGLTest::renderSetup, &FlatGLTest::renderTeardown); - addInstancedTests({&FlatGLTest::renderAlpha2D, - &FlatGLTest::renderAlpha3D}, + addInstancedTests({ + &FlatGLTest::renderAlpha2D, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderAlpha2D, + #endif + &FlatGLTest::renderAlpha3D, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderAlpha3D + #endif + }, Containers::arraySize(RenderAlphaData), &FlatGLTest::renderAlphaSetup, &FlatGLTest::renderAlphaTeardown); #ifndef MAGNUM_TARGET_GLES2 - addInstancedTests({&FlatGLTest::renderObjectId2D, - &FlatGLTest::renderObjectId3D}, + addInstancedTests({ + &FlatGLTest::renderObjectId2D, + &FlatGLTest::renderObjectId2D, + &FlatGLTest::renderObjectId3D, + &FlatGLTest::renderObjectId3D}, Containers::arraySize(RenderObjectIdData), &FlatGLTest::renderObjectIdSetup, &FlatGLTest::renderObjectIdTeardown); #endif - addTests({&FlatGLTest::renderInstanced2D, - &FlatGLTest::renderInstanced3D}, + addTests({ + &FlatGLTest::renderInstanced2D, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderInstanced2D, + #endif + &FlatGLTest::renderInstanced3D, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderInstanced3D + #endif + }, &FlatGLTest::renderSetup, &FlatGLTest::renderTeardown); + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({&FlatGLTest::renderMulti2D, + &FlatGLTest::renderMulti3D}, + Containers::arraySize(RenderMultiData), + &FlatGLTest::renderObjectIdSetup, + &FlatGLTest::renderObjectIdTeardown); + #endif + /* 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 @@ -322,6 +494,35 @@ template void FlatGLTest::construct() { MAGNUM_VERIFY_NO_GL_ERROR(); } +#ifndef MAGNUM_TARGET_GLES2 +template void FlatGLTest::constructUniformBuffers() { + setTestCaseTemplateName(std::to_string(dimensions)); + + auto&& data = ConstructUniformBuffersData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if((data.flags & FlatGL2D::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported()) + 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."); + #endif + + FlatGL shader{data.flags, data.drawCount}; + CORRADE_COMPARE(shader.flags(), data.flags); + CORRADE_COMPARE(shader.drawCount(), data.drawCount); + CORRADE_VERIFY(shader.id()); + { + #ifdef CORRADE_TARGET_APPLE + CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); + #endif + CORRADE_VERIFY(shader.validate().first); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); +} +#endif + template void FlatGLTest::constructMove() { setTestCaseTemplateName(std::to_string(dimensions)); @@ -343,6 +544,36 @@ template void FlatGLTest::constructMove() { CORRADE_VERIFY(!b.id()); } +#ifndef MAGNUM_TARGET_GLES2 +template void FlatGLTest::constructMoveUniformBuffers() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + FlatGL a{FlatGL::Flag::UniformBuffers, 5}; + const GLuint id = a.id(); + CORRADE_VERIFY(id); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + FlatGL b{std::move(a)}; + CORRADE_COMPARE(b.id(), id); + CORRADE_COMPARE(b.flags(), FlatGL::Flag::UniformBuffers); + CORRADE_COMPARE(b.drawCount(), 5); + CORRADE_VERIFY(!a.id()); + + FlatGL c{NoCreate}; + c = std::move(b); + CORRADE_COMPARE(c.id(), id); + CORRADE_COMPARE(c.flags(), FlatGL::Flag::UniformBuffers); + CORRADE_COMPARE(c.drawCount(), 5); + CORRADE_VERIFY(!b.id()); +} +#endif + template void FlatGLTest::constructTextureTransformationNotTextured() { setTestCaseTemplateName(std::to_string(dimensions)); @@ -357,6 +588,87 @@ template void FlatGLTest::constructTextureTransformation "Shaders::FlatGL: texture transformation enabled but the shader is not textured\n"); } +#ifndef MAGNUM_TARGET_GLES2 +template void FlatGLTest::constructUniformBuffersZeroDraws() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + FlatGL{FlatGL::Flag::UniformBuffers, 0}; + CORRADE_COMPARE(out.str(), + "Shaders::FlatGL: draw count can't be zero\n"); +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +template void FlatGLTest::setUniformUniformBuffersEnabled() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + FlatGL shader{FlatGL::Flag::UniformBuffers}; + shader.setTransformationProjectionMatrix({}) + .setTextureMatrix({}) + .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::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"); +} + +template void FlatGLTest::bindBufferUniformBuffersNotEnabled() { + 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}; + + GL::Buffer buffer; + FlatGL shader; + shader.bindTransformationProjectionBuffer(buffer) + .bindTransformationProjectionBuffer(buffer, 0, 16) + .bindDrawBuffer(buffer) + .bindDrawBuffer(buffer, 0, 16) + .bindTextureTransformationBuffer(buffer) + .bindTextureTransformationBuffer(buffer, 0, 16) + .setDrawOffset(0); + CORRADE_COMPARE(out.str(), + "Shaders::FlatGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::FlatGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::FlatGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::FlatGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::FlatGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::FlatGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::FlatGL::setDrawOffset(): the shader was not created with uniform buffers enabled\n"); +} +#endif + template void FlatGLTest::bindTextureNotEnabled() { setTestCaseTemplateName(std::to_string(dimensions)); @@ -408,6 +720,32 @@ template void FlatGLTest::setTextureMatrixNotEnabled() { "Shaders::FlatGL::setTextureMatrix(): the shader was not created with texture transformation enabled\n"); } +#ifndef MAGNUM_TARGET_GLES2 +template void FlatGLTest::bindTextureTransformBufferNotEnabled() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + GL::Buffer buffer{GL::Buffer::TargetHint::Uniform}; + FlatGL shader{FlatGL::Flag::UniformBuffers}; + shader.bindTextureTransformationBuffer(buffer) + .bindTextureTransformationBuffer(buffer, 0, 16); + CORRADE_COMPARE(out.str(), + "Shaders::FlatGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled\n" + "Shaders::FlatGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled\n"); +} +#endif + #ifndef MAGNUM_TARGET_GLES2 template void FlatGLTest::setObjectIdNotEnabled() { setTestCaseTemplateName(std::to_string(dimensions)); @@ -427,6 +765,28 @@ template void FlatGLTest::setObjectIdNotEnabled() { } #endif +#ifndef MAGNUM_TARGET_GLES2 +template void FlatGLTest::setWrongDrawOffset() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + FlatGL{FlatGL::Flag::UniformBuffers, 5} + .setDrawOffset(5); + CORRADE_COMPARE(out.str(), + "Shaders::FlatGL::setDrawOffset(): draw offset 5 is out of bounds for 5 draws\n"); +} +#endif + constexpr Vector2i RenderSize{80, 80}; void FlatGLTest::renderSetup() { @@ -454,11 +814,40 @@ void FlatGLTest::renderTeardown() { _color = GL::Renderbuffer{NoCreate}; } -void FlatGLTest::renderDefaults2D() { +template void FlatGLTest::renderDefaults2D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32)); - FlatGL2D{} - .draw(circle); + FlatGL2D shader{flag}; + + if(flag == FlatGL2D::Flag{}) { + shader.draw(circle); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == FlatGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + FlatDrawUniform{} + }}; + shader + .bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(circle); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -474,11 +863,40 @@ void FlatGLTest::renderDefaults2D() { (DebugTools::CompareImageToFile{_manager, 238.0f, 0.2975f})); } -void FlatGLTest::renderDefaults3D() { +template void FlatGLTest::renderDefaults3D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); - FlatGL3D{} - .draw(sphere); + FlatGL3D shader{flag}; + + if(flag == FlatGL3D::Flag{}) { + shader.draw(sphere); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == FlatGL3D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform3D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + FlatDrawUniform{} + }}; + shader + .bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -494,13 +912,45 @@ void FlatGLTest::renderDefaults3D() { (DebugTools::CompareImageToFile{_manager, 238.0f, 0.2975f})); } -void FlatGLTest::renderColored2D() { +template void FlatGLTest::renderColored2D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32)); - FlatGL2D{} - .setColor(0x9999ff_rgbf) - .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) - .draw(circle); + FlatGL2D shader{flag}; + + if(flag == FlatGL2D::Flag{}) { + shader + .setColor(0x9999ff_rgbf) + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + .draw(circle); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == FlatGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + FlatDrawUniform{} + .setColor(0x9999ff_rgbf) + }}; + shader + .bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(circle); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -521,17 +971,54 @@ void FlatGLTest::renderColored2D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void FlatGLTest::renderColored3D() { +template void FlatGLTest::renderColored3D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); - FlatGL3D{} - .setColor(0x9999ff_rgbf) - .setTransformationProjectionMatrix( - Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* - Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)) - .draw(sphere); + FlatGL3D shader{flag}; + + if(flag == FlatGL3D::Flag{}) { + shader + .setColor(0x9999ff_rgbf) + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)) + .draw(sphere); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == FlatGL3D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + FlatDrawUniform{} + .setColor(0x9999ff_rgbf) + }}; + shader + .bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -568,7 +1055,18 @@ constexpr GL::TextureFormat TextureFormatRGBA = #endif ; -void FlatGLTest::renderSinglePixelTextured2D() { +template void FlatGLTest::renderSinglePixelTextured2D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32, Primitives::Circle2DFlag::TextureCoordinates)); @@ -581,10 +1079,28 @@ void FlatGLTest::renderSinglePixelTextured2D() { .setStorage(1, TextureFormatRGBA, Vector2i{1}) .setSubImage(0, {}, diffuseImage); - FlatGL2D{FlatGL3D::Flag::Textured} - .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) - .bindTexture(texture) - .draw(circle); + FlatGL2D shader{FlatGL2D::Flag::Textured|flag}; + shader.bindTexture(texture); + + if(flag == FlatGL2D::Flag{}) { + shader.setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + .draw(circle); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == FlatGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + FlatDrawUniform{} + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(circle); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -606,7 +1122,18 @@ void FlatGLTest::renderSinglePixelTextured2D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void FlatGLTest::renderSinglePixelTextured3D() { +template void FlatGLTest::renderSinglePixelTextured3D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, Primitives::UVSphereFlag::TextureCoordinates)); @@ -619,14 +1146,38 @@ void FlatGLTest::renderSinglePixelTextured3D() { .setStorage(1, TextureFormatRGBA, Vector2i{1}) .setSubImage(0, {}, diffuseImage); - FlatGL3D{FlatGL3D::Flag::Textured} - .setTransformationProjectionMatrix( - Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* - Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)) - .bindTexture(texture) - .draw(sphere); + FlatGL3D shader{FlatGL3D::Flag::Textured|flag}; + shader.bindTexture(texture); + + if(flag == FlatGL3D::Flag{}) { + shader.setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf) + ) + .draw(sphere); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == FlatGL3D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + FlatDrawUniform{} + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -648,10 +1199,21 @@ void FlatGLTest::renderSinglePixelTextured3D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void FlatGLTest::renderTextured2D() { +template void FlatGLTest::renderTextured2D() { auto&& data = RenderTexturedData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -671,18 +1233,40 @@ void FlatGLTest::renderTextured2D() { .setStorage(1, TextureFormatRGB, image->size()) .setSubImage(0, {}, *image); - FlatGL2D shader{data.flags}; - shader - .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) - /* Colorized. Case without a color (where it should be white) is tested - in renderSinglePixelTextured() */ - .setColor(0x9999ff_rgbf) - .bindTexture(texture); - - if(data.textureTransformation != Matrix3{}) - shader.setTextureMatrix(data.textureTransformation); + FlatGL2D shader{data.flags|flag}; + shader.bindTexture(texture); - shader.draw(circle); + if(flag == FlatGL2D::Flag{}) { + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); + shader.setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + /* Colorized. Case without a color (where it should be white) is + tested in renderSinglePixelTextured2D() */ + .setColor(0x9999ff_rgbf) + .draw(circle); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == FlatGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + FlatDrawUniform{} + .setColor(0x9999ff_rgbf) + }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setTextureMatrix(data.textureTransformation) + }}; + if(data.flags & FlatGL2D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(circle); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -704,10 +1288,21 @@ void FlatGLTest::renderTextured2D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void FlatGLTest::renderTextured3D() { +template void FlatGLTest::renderTextured3D() { auto&& data = RenderTexturedData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -727,22 +1322,50 @@ void FlatGLTest::renderTextured3D() { .setStorage(1, TextureFormatRGB, image->size()) .setSubImage(0, {}, *image); - FlatGL3D shader{data.flags}; - shader - .setTransformationProjectionMatrix( - Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* - Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(data.flip ? 15.0_degf : -15.0_degf)* - Matrix4::rotationX(data.flip ? -15.0_degf : 15.0_degf)) - /* Colorized. Case without a color (where it should be white) is tested - in renderSinglePixelTextured() */ - .setColor(0x9999ff_rgbf) - .bindTexture(texture); - - if(data.textureTransformation != Matrix3{}) - shader.setTextureMatrix(data.textureTransformation); + FlatGL3D shader{data.flags|flag}; + shader.bindTexture(texture); - shader.draw(sphere); + if(flag == FlatGL3D::Flag{}) { + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); + shader + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(data.flip ? 15.0_degf : -15.0_degf)* + Matrix4::rotationX(data.flip ? -15.0_degf : 15.0_degf)) + /* Colorized. Case without a color (where it should be white) is + tested in renderSinglePixelTextured3D() */ + .setColor(0x9999ff_rgbf) + .draw(sphere); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == FlatGL3D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(data.flip ? 15.0_degf : -15.0_degf)* + Matrix4::rotationX(data.flip ? -15.0_degf : 15.0_degf) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + FlatDrawUniform{} + .setColor(0x9999ff_rgbf) + }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setTextureMatrix(data.textureTransformation) + }}; + if(data.flags & FlatGL3D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -764,8 +1387,20 @@ void FlatGLTest::renderTextured3D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -template void FlatGLTest::renderVertexColor2D() { - setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); +template void FlatGLTest::renderVertexColor2D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"}); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } else + #endif + { + setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); + } if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) @@ -796,11 +1431,30 @@ template void FlatGLTest::renderVertexColor2D() { .setStorage(1, TextureFormatRGB, image->size()) .setSubImage(0, {}, *image); - FlatGL2D{FlatGL2D::Flag::Textured|FlatGL2D::Flag::VertexColor} - .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) - .setColor(0x9999ff_rgbf) - .bindTexture(texture) - .draw(circle); + FlatGL2D shader{FlatGL2D::Flag::Textured|FlatGL2D::Flag::VertexColor|flag}; + shader.bindTexture(texture); + + if(flag == FlatGL2D::Flag{}) { + shader.setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + .setColor(0x9999ff_rgbf) + .draw(circle); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == FlatGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + FlatDrawUniform{} + .setColor(0x9999ff_rgbf) + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(circle); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -818,8 +1472,20 @@ template void FlatGLTest::renderVertexColor2D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -template void FlatGLTest::renderVertexColor3D() { - setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); +template void FlatGLTest::renderVertexColor3D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"}); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } else + #endif + { + setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); + } if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) @@ -850,15 +1516,39 @@ template void FlatGLTest::renderVertexColor3D() { .setStorage(1, TextureFormatRGB, image->size()) .setSubImage(0, {}, *image); - FlatGL3D{FlatGL3D::Flag::Textured|FlatGL3D::Flag::VertexColor} - .setTransformationProjectionMatrix( - Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* - Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)) - .setColor(0x9999ff_rgbf) - .bindTexture(texture) - .draw(sphere); + FlatGL3D shader{FlatGL3D::Flag::Textured|FlatGL3D::Flag::VertexColor|flag}; + shader.bindTexture(texture); + + if(flag == FlatGL2D::Flag{}) { + shader.setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)) + .setColor(0x9999ff_rgbf) + .draw(sphere); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == FlatGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + FlatDrawUniform{} + .setColor(0x9999ff_rgbf) + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -890,10 +1580,21 @@ void FlatGLTest::renderAlphaTeardown() { renderTeardown(); } -void FlatGLTest::renderAlpha2D() { +template void FlatGLTest::renderAlpha2D() { auto&& data = RenderAlphaData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -915,15 +1616,35 @@ void FlatGLTest::renderAlpha2D() { GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32, Primitives::Circle2DFlag::TextureCoordinates)); - FlatGL2D shader{data.flags}; - shader.setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) - .setColor(0x9999ff_rgbf) - .bindTexture(texture); - - if(data.flags & FlatGL3D::Flag::AlphaMask) - shader.setAlphaMask(data.threshold); + FlatGL2D shader{data.flags|flag}; + shader.bindTexture(texture); - shader.draw(circle); + if(flag == FlatGL2D::Flag{}) { + /* Test that the default is correct by not setting the threshold if + it's equal to the default */ + if(data.flags & FlatGL2D::Flag::AlphaMask && data.threshold != 0.5f) + shader.setAlphaMask(data.threshold); + shader.setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + .setColor(0x9999ff_rgbf) + .draw(circle); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == FlatGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + FlatDrawUniform{} + .setColor(0x9999ff_rgbf) + .setAlphaMask(data.threshold) + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(circle); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -941,10 +1662,21 @@ void FlatGLTest::renderAlpha2D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void FlatGLTest::renderAlpha3D() { +template void FlatGLTest::renderAlpha3D() { auto&& data = RenderAlphaData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -966,23 +1698,55 @@ void FlatGLTest::renderAlpha3D() { GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, Primitives::UVSphereFlag::TextureCoordinates)); - FlatGL3D shader{data.flags}; - shader.setTransformationProjectionMatrix( - Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* - Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)) - .setColor(0x9999ff_rgbf) - .bindTexture(texture); - - if(data.flags & FlatGL3D::Flag::AlphaMask) - shader.setAlphaMask(data.threshold); + FlatGL3D shader{data.flags|flag}; + shader.bindTexture(texture); - /* For proper Z order draw back faces first and then front faces */ - GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Front); - shader.draw(sphere); - GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Back); - shader.draw(sphere); + if(flag == FlatGL2D::Flag{}) { + shader.setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)) + .setColor(0x9999ff_rgbf); + + /* Test that the default is correct by not setting the threshold if + it's equal to the default */ + if(data.flags & FlatGL3D::Flag::AlphaMask && data.threshold != 0.5f) + shader.setAlphaMask(data.threshold); + + /* For proper Z order draw back faces first and then front faces */ + GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Front); + shader.draw(sphere); + GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Back); + shader.draw(sphere); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == FlatGL3D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + FlatDrawUniform{} + .setColor(0x9999ff_rgbf) + .setAlphaMask(data.threshold) + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform); + + /* For proper Z order draw back faces first and then front faces */ + GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Front); + shader.draw(sphere); + GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Back); + shader.draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1039,10 +1803,21 @@ void FlatGLTest::renderObjectIdTeardown() { _framebuffer = GL::Framebuffer{NoCreate}; } -void FlatGLTest::renderObjectId2D() { +template void FlatGLTest::renderObjectId2D() { auto&& data = RenderObjectIdData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); @@ -1058,11 +1833,27 @@ void FlatGLTest::renderObjectId2D() { GL::Buffer{Containers::arrayView({11002u, 48823u})}, 1, 0, FlatGL2D::ObjectId{}); - FlatGL2D{data.flags} - .setColor(0x9999ff_rgbf) - .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) - .setObjectId(data.uniformId) - .draw(circle); + FlatGL2D shader{data.flags|flag}; + + if(flag == FlatGL2D::Flag{}) { + shader.setColor(0x9999ff_rgbf) + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + .setObjectId(data.uniformId) + .draw(circle); + } else if(flag == FlatGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + FlatDrawUniform{} + .setColor(0x9999ff_rgbf) + .setObjectId(data.uniformId) + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(circle); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1097,10 +1888,21 @@ void FlatGLTest::renderObjectId2D() { CORRADE_COMPARE(image.pixels()[40][46], data.expected); } -void FlatGLTest::renderObjectId3D() { +template void FlatGLTest::renderObjectId3D() { auto&& data = RenderObjectIdData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); @@ -1116,15 +1918,36 @@ void FlatGLTest::renderObjectId3D() { GL::Buffer{Containers::arrayView({11002u, 48823u})}, 1, 0, FlatGL2D::ObjectId{}); - FlatGL3D{data.flags} - .setColor(0x9999ff_rgbf) - .setTransformationProjectionMatrix( - Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* - Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)) - .setObjectId(data.uniformId) - .draw(sphere); + FlatGL3D shader{data.flags|flag}; + + if(flag == FlatGL3D::Flag{}) { + shader.setColor(0x9999ff_rgbf) + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)) + .setObjectId(data.uniformId) + .draw(sphere); + } else if(flag == FlatGL3D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + FlatDrawUniform{} + .setColor(0x9999ff_rgbf) + .setObjectId(data.uniformId) + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(sphere); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1163,7 +1986,18 @@ void FlatGLTest::renderObjectId3D() { } #endif -void FlatGLTest::renderInstanced2D() { +template void FlatGLTest::renderInstanced2D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::instanced_arrays::string() << "is not supported."); @@ -1219,17 +2053,44 @@ void FlatGLTest::renderInstanced2D() { .setStorage(1, TextureFormatRGB, image->size()) .setSubImage(0, {}, *image); - FlatGL2D{FlatGL2D::Flag::Textured| + FlatGL2D shader{FlatGL2D::Flag::Textured| FlatGL2D::Flag::VertexColor| FlatGL2D::Flag::InstancedTransformation| - FlatGL2D::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); + FlatGL2D::Flag::InstancedTextureOffset|flag}; + shader.bindTexture(texture); + + if(flag == FlatGL2D::Flag{}) { + shader.setColor(0xffff99_rgbf) + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})) + .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) + .draw(circle); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == FlatGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f}) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + FlatDrawUniform{} + .setColor(0xffff99_rgbf) + }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindTextureTransformationBuffer(textureTransformationUniform) + .draw(circle); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1247,7 +2108,18 @@ void FlatGLTest::renderInstanced2D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void FlatGLTest::renderInstanced3D() { +template void FlatGLTest::renderInstanced3D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == FlatGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::instanced_arrays::string() << "is not supported."); @@ -1303,18 +2175,46 @@ void FlatGLTest::renderInstanced3D() { .setStorage(1, TextureFormatRGB, image->size()) .setSubImage(0, {}, *image); - FlatGL3D{FlatGL3D::Flag::Textured| + FlatGL3D shader{FlatGL3D::Flag::Textured| FlatGL3D::Flag::VertexColor| FlatGL3D::Flag::InstancedTransformation| - FlatGL3D::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); + FlatGL3D::Flag::InstancedTextureOffset|flag}; + shader.bindTexture(texture); + + if(flag == FlatGL3D::Flag{}) { + shader.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})) + .draw(sphere); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == FlatGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f}) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + FlatDrawUniform{} + .setColor(0xffff99_rgbf) + }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindTextureTransformationBuffer(textureTransformationUniform) + .draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1332,6 +2232,421 @@ void FlatGLTest::renderInstanced3D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } +#ifndef MAGNUM_TARGET_GLES2 +void FlatGLTest::renderMulti2D() { + auto&& data = RenderMultiData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + GL::Texture2D texture; + if(data.flags & FlatGL2D::Flag::Textured) { + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + 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); + } + + /* Circle is a fan, plane is a strip, make it indexed first */ + Trade::MeshData circleData = MeshTools::generateIndices(Primitives::circle2DSolid(32, + Primitives::Circle2DFlag::TextureCoordinates)); + Trade::MeshData squareData = MeshTools::generateIndices(Primitives::squareSolid( + Primitives::SquareFlag::TextureCoordinates)); + Trade::MeshData triangleData = MeshTools::generateIndices(Primitives::circle2DSolid(3, + Primitives::Circle2DFlag::TextureCoordinates)); + GL::Mesh mesh = MeshTools::compile(MeshTools::concatenate({circleData, squareData, triangleData})); + GL::MeshView circle{mesh}; + circle.setCount(circleData.indexCount()); + GL::MeshView square{mesh}; + square.setCount(squareData.indexCount()) + .setIndexRange(circleData.indexCount()); + GL::MeshView triangle{mesh}; + triangle.setCount(triangleData.indexCount()) + .setIndexRange(circleData.indexCount() + squareData.indexCount()); + + /* Some drivers have uniform offset alignment as high as 256, which means + the subsequent sets of uniforms have to be aligned to a multiply of it. + The data.uniformIncrement is set high enough to ensure that, in the + non-offset-bind case this value is 1. */ + + Containers::Array transformationProjectionData{2*data.uniformIncrement + 1}; + transformationProjectionData[0*data.uniformIncrement] = TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})* + Matrix3::translation({-1.25f, -1.25f}) + ); + transformationProjectionData[1*data.uniformIncrement] = TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})* + Matrix3::translation({ 1.25f, -1.25f}) + ); + transformationProjectionData[2*data.uniformIncrement] = TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})* + Matrix3::translation({ 0.00f, 1.25f}) + ); + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, transformationProjectionData}; + + Containers::Array textureTransformationData{2*data.uniformIncrement + 1}; + textureTransformationData[0*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({0.0f, 0.0f}) + ); + textureTransformationData[1*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({1.0f, 0.0f}) + ); + textureTransformationData[2*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({0.5f, 1.0f}) + ); + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; + + Containers::Array drawData{2*data.uniformIncrement + 1}; + drawData[0*data.uniformIncrement] = FlatDrawUniform{} + .setColor(data.flags & FlatGL2D::Flag::Textured ? + 0xffffff_rgbf : 0xff0000_rgbf) + .setObjectId(1211); + drawData[1*data.uniformIncrement] = FlatDrawUniform{} + .setColor(data.flags & FlatGL2D::Flag::Textured ? + 0xffffff_rgbf : 0x0000ff_rgbf) + .setObjectId(5627); + drawData[2*data.uniformIncrement] = FlatDrawUniform{} + .setColor(data.flags & FlatGL2D::Flag::Textured ? + 0xffffff_rgbf : 0xff0000_rgbf) + .setObjectId(36363); + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; + + FlatGL2D shader{FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::ObjectId|data.flags, data.drawCount}; + if(data.flags & FlatGL2D::Flag::Textured) + shader.bindTexture(texture); + + /* Just one draw, rebinding UBOs each time */ + if(data.drawCount == 1) { + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 0*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), + sizeof(TransformationProjectionUniform2D)); + shader.bindDrawBuffer(drawUniform, + 0*data.uniformIncrement*sizeof(FlatDrawUniform), + sizeof(FlatDrawUniform)); + if(data.flags & FlatGL2D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 0*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(circle); + + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 1*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), + sizeof(TransformationProjectionUniform2D)); + shader.bindDrawBuffer(drawUniform, + 1*data.uniformIncrement*sizeof(FlatDrawUniform), + sizeof(FlatDrawUniform)); + if(data.flags & FlatGL2D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 1*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(square); + + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 2*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), + sizeof(TransformationProjectionUniform2D)); + shader.bindDrawBuffer(drawUniform, + 2*data.uniformIncrement*sizeof(FlatDrawUniform), + sizeof(FlatDrawUniform)); + if(data.flags & FlatGL2D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 2*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(triangle); + + /* Otherwise using the draw offset */ + } else { + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform); + if(data.flags & FlatGL2D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); + shader.setDrawOffset(0) + .draw(circle); + shader.setDrawOffset(1) + .draw(square); + shader.setDrawOffset(2) + .draw(triangle); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + /* + Colored case: + + - Circle should be lower left, red + - Square lower right, blue + - Triangle up center, red + + Textured case: + + - Circle should have bottom left numbers, so light 7881 + - Square bottom right, 1223 + - Triangle 6778 + */ + 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", data.expected2D}), + (DebugTools::CompareImageToFile{_manager, data.maxThreshold, data.meanThreshold})); + + /* Object ID -- no need to verify the whole image, just check that pixels + on known places have expected values. SwiftShader insists that the read + format has to be 32bit, so the renderbuffer format is that too to make + it the same (ES3 Mesa complains if these don't match). */ + #ifndef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + #endif + { + _framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1}); + CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete); + Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI}); + MAGNUM_VERIFY_NO_GL_ERROR(); + CORRADE_COMPARE(image.pixels()[5][5], 27); /* Outside */ + CORRADE_COMPARE(image.pixels()[24][24], 1211); /* Circle */ + CORRADE_COMPARE(image.pixels()[24][56], 5627); /* Square */ + CORRADE_COMPARE(image.pixels()[56][40], 36363); /* Triangle */ + } +} + +void FlatGLTest::renderMulti3D() { + auto&& data = RenderMultiData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + GL::Texture2D texture; + 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."); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + 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); + } + + Trade::MeshData sphereData = Primitives::uvSphereSolid(16, 32, + Primitives::UVSphereFlag::TextureCoordinates); + /* Plane is a strip, make it indexed first */ + Trade::MeshData planeData = MeshTools::generateIndices(Primitives::planeSolid( + Primitives::PlaneFlag::TextureCoordinates)); + Trade::MeshData coneData = Primitives::coneSolid(1, 32, 1.0f, + Primitives::ConeFlag::TextureCoordinates); + GL::Mesh mesh = MeshTools::compile(MeshTools::concatenate({sphereData, planeData, coneData})); + GL::MeshView sphere{mesh}; + sphere.setCount(sphereData.indexCount()); + GL::MeshView plane{mesh}; + plane.setCount(planeData.indexCount()) + .setIndexRange(sphereData.indexCount()); + GL::MeshView cone{mesh}; + cone.setCount(coneData.indexCount()) + .setIndexRange(sphereData.indexCount() + planeData.indexCount()); + + /* Some drivers have uniform offset alignment as high as 256, which means + the subsequent sets of uniforms have to be aligned to a multiply of it. + The data.uniformIncrement is set high enough to ensure that, in the + non-offset-bind case this value is 1. */ + + Containers::Array transformationProjectionData{2*data.uniformIncrement + 1}; + transformationProjectionData[0*data.uniformIncrement] = TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({-1.25f, -1.25f, 0.0f})* + /* To be consistent with Phong's output where it tests that the + normal matrix is applied properly */ + Matrix4::rotationX(90.0_degf) + ); + transformationProjectionData[1*data.uniformIncrement] = TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({ 1.25f, -1.25f, 0.0f}) + ); + transformationProjectionData[2*data.uniformIncrement] = TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({ 0.0f, 1.0f, 1.0f}) + ); + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, transformationProjectionData}; + + Containers::Array textureTransformationData{2*data.uniformIncrement + 1}; + textureTransformationData[0*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({0.0f, 0.0f}) + ); + textureTransformationData[1*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({1.0f, 0.0f}) + ); + textureTransformationData[2*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({0.5f, 1.0f}) + ); + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; + + Containers::Array drawData{2*data.uniformIncrement + 1}; + drawData[0*data.uniformIncrement] = FlatDrawUniform{} + .setColor(data.flags & FlatGL3D::Flag::Textured ? + 0xffffff_rgbf : 0xff0000_rgbf) + .setObjectId(1211); + drawData[1*data.uniformIncrement] = FlatDrawUniform{} + .setColor(data.flags & FlatGL3D::Flag::Textured ? + 0xffffff_rgbf : 0x0000ff_rgbf) + .setObjectId(5627); + drawData[2*data.uniformIncrement] = FlatDrawUniform{} + .setColor(data.flags & FlatGL3D::Flag::Textured ? + 0xffffff_rgbf : 0xff0000_rgbf) + .setObjectId(36363); + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; + + FlatGL3D shader{FlatGL3D::Flag::UniformBuffers|FlatGL3D::Flag::ObjectId|data.flags, data.drawCount}; + if(data.flags & FlatGL3D::Flag::Textured) + shader.bindTexture(texture); + + /* Just one draw, rebinding UBOs each time */ + if(data.drawCount == 1) { + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 0*data.uniformIncrement*sizeof(TransformationProjectionUniform3D), + sizeof(TransformationProjectionUniform3D)); + shader.bindDrawBuffer(drawUniform, + 0*data.uniformIncrement*sizeof(FlatDrawUniform), + sizeof(FlatDrawUniform)); + if(data.flags & FlatGL3D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 0*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(sphere); + + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 1*data.uniformIncrement*sizeof(TransformationProjectionUniform3D), + sizeof(TransformationProjectionUniform3D)); + shader.bindDrawBuffer(drawUniform, + 1*data.uniformIncrement*sizeof(FlatDrawUniform), + sizeof(FlatDrawUniform)); + if(data.flags & FlatGL3D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 1*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(plane); + + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 2*data.uniformIncrement*sizeof(TransformationProjectionUniform3D), + sizeof(TransformationProjectionUniform3D)); + shader.bindDrawBuffer(drawUniform, + 2*data.uniformIncrement*sizeof(FlatDrawUniform), + sizeof(FlatDrawUniform)); + if(data.flags & FlatGL3D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 2*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(cone); + + /* Otherwise using the draw offset */ + } else { + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform); + if(data.flags & FlatGL3D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + /* + Colored case: + + - Sphere should be lower left, red + - Plane lower right, blue + - Cone up center, red + + Textured case: + + - Sphere should have bottom left numbers, so light 7881, rotated (78 + visible) + - Plane bottom right, 1223 + - Cone 6778 + */ + 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", data.expected3D}), + (DebugTools::CompareImageToFile{_manager, data.maxThreshold, data.meanThreshold})); + + /* Object ID -- no need to verify the whole image, just check that pixels + on known places have expected values. SwiftShader insists that the read + format has to be 32bit, so the renderbuffer format is that too to make + it the same (ES3 Mesa complains if these don't match). */ + #ifndef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + #endif + { + _framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1}); + CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete); + Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI}); + MAGNUM_VERIFY_NO_GL_ERROR(); + CORRADE_COMPARE(image.pixels()[5][5], 27); /* Outside */ + CORRADE_COMPARE(image.pixels()[24][24], 1211); /* Sphere */ + CORRADE_COMPARE(image.pixels()[24][56], 5627); /* Plane */ + CORRADE_COMPARE(image.pixels()[56][40], 36363); /* Circle */ + } +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::FlatGLTest) diff --git a/src/Magnum/Shaders/Test/FlatGL_Test.cpp b/src/Magnum/Shaders/Test/FlatGL_Test.cpp index 566ac3eb3..640e18d78 100644 --- a/src/Magnum/Shaders/Test/FlatGL_Test.cpp +++ b/src/Magnum/Shaders/Test/FlatGL_Test.cpp @@ -78,8 +78,8 @@ template void FlatGL_Test::constructCopy() { void FlatGL_Test::debugFlag() { std::ostringstream out; - Debug{&out} << FlatGL3D::Flag::Textured << FlatGL3D::Flag(0xf0); - CORRADE_COMPARE(out.str(), "Shaders::FlatGL::Flag::Textured Shaders::FlatGL::Flag(0xf0)\n"); + Debug{&out} << FlatGL3D::Flag::Textured << FlatGL3D::Flag(0xf00d); + CORRADE_COMPARE(out.str(), "Shaders::FlatGL::Flag::Textured Shaders::FlatGL::Flag(0xf00d)\n"); } void FlatGL_Test::debugFlags() { diff --git a/src/Magnum/Shaders/Test/FlatTest.cpp b/src/Magnum/Shaders/Test/FlatTest.cpp new file mode 100644 index 000000000..8365429ca --- /dev/null +++ b/src/Magnum/Shaders/Test/FlatTest.cpp @@ -0,0 +1,124 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include + +#include "Magnum/Shaders/Flat.h" + +namespace Magnum { namespace Shaders { namespace Test { namespace { + +struct FlatTest: TestSuite::Tester { + explicit FlatTest(); + + template void uniformSize(); + + void drawUniformConstructDefault(); + void drawUniformConstructNoInit(); + void drawUniformSetters(); +}; + +FlatTest::FlatTest() { + addTests({&FlatTest::uniformSize, + + &FlatTest::drawUniformConstructDefault, + &FlatTest::drawUniformConstructNoInit, + &FlatTest::drawUniformSetters}); +} + +using namespace Math::Literals; + +template struct UniformTraits; +template<> struct UniformTraits { + static const char* name() { return "FlatDrawUniform"; } +}; + +template void FlatTest::uniformSize() { + setTestCaseTemplateName(UniformTraits::name()); + + CORRADE_FAIL_IF(sizeof(T) % sizeof(Vector4) != 0, sizeof(T) << "is not a multiple of vec4 for UBO alignment."); + + /* 48-byte structures are fine, we'll align them to 768 bytes and not + 256, but warn about that */ + CORRADE_FAIL_IF(768 % sizeof(T) != 0, sizeof(T) << "can't fit exactly into 768-byte UBO alignment."); + if(256 % sizeof(T) != 0) + CORRADE_WARN(sizeof(T) << "can't fit exactly into 256-byte UBO alignment, only 768."); +} + +void FlatTest::drawUniformConstructDefault() { + FlatDrawUniform a; + FlatDrawUniform b{DefaultInit}; + CORRADE_COMPARE(a.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(b.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(a.objectId, 0); + CORRADE_COMPARE(b.objectId, 0); + CORRADE_COMPARE(a.alphaMask, 0.5f); + CORRADE_COMPARE(b.alphaMask, 0.5f); + + constexpr FlatDrawUniform ca; + constexpr FlatDrawUniform cb{DefaultInit}; + CORRADE_COMPARE(ca.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(cb.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(ca.objectId, 0); + CORRADE_COMPARE(cb.objectId, 0); + CORRADE_COMPARE(ca.alphaMask, 0.5f); + CORRADE_COMPARE(cb.alphaMask, 0.5f); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void FlatTest::drawUniformConstructNoInit() { + /* Testing only some fields, should be enough */ + FlatDrawUniform a; + a.color = 0x354565fc_rgbaf; + a.alphaMask = 0.7f; + + new(&a) FlatDrawUniform{NoInit}; + CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); + CORRADE_COMPARE(a.alphaMask, 0.7f); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void FlatTest::drawUniformSetters() { + FlatDrawUniform a; + a.setColor(0x354565fc_rgbaf) + .setObjectId(7) + .setAlphaMask(0.7f); + CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); + CORRADE_COMPARE(a.objectId, 7); + CORRADE_COMPARE(a.alphaMask, 0.7f); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Shaders::Test::FlatTest) diff --git a/src/Magnum/Shaders/Test/FlatTestFiles/multidraw-textured2D.tga b/src/Magnum/Shaders/Test/FlatTestFiles/multidraw-textured2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..31d45ce3ef91c561239c55ca5e56926d811e0901 GIT binary patch literal 2501 zcmcIlTT>iG6yBL-waPc%{1?ma_RQYQ4=`$h0ST4F5HUbfrHG2bki><(4rI9+A`lD# z#1Mih3kaY{vVm-X03m9sEG{or>5IR@@9Ui*6I>rNHC=Nq-QRcmoYT_|$7>GjDRGoI zUUzI278d3kov_FE_V(ictc`U;?x_7O+~M9@NkDGLNWzzdkMr!l%g_z@4EQ+D?rVeI z0-v(8vx83pqZQNA=CmZ>759@zWHFgq;c{WxQs(}Z&h+Sw>#1l2suh^>cXnj*%7ubh z{YiXw76yQZ737H{Ue}4SKyibp=+*I7Erwu7e)2D#gW^=+5-@HGIM%6b*r}QUbdaHE8W^qcXoDW+KgVZY%dJ-TsmW8VJV$u zhW2)?@6;S?OIqSN#mKc#ZDpXb9tw*sfdSqDybd6+!uHmu|E2iAMIOnAI66FHb3c}s z;FEd!b7|<0`97dila)XVm9^<_9`mx1p0XrS4E6w%qj;w#LdVLS;qnrEin5Eau_AE< z>+*@r8~Wix+FOV8!y&B>rkJ@0wi_9g?5)dovdXDI^ zX;M4dtG`p{Gco0XMtsWiP$i~EYy`DW@?fg>Mw5R%90n{IWX5ZrhD#bn^=A0Qc;JIy zn2i>UJjf|V>l4#L+=gW3R+fg*HrKsxgMGDnEp)V-DXq$cj(^7_;Kk1J0L$um#5z#% z`ovu!zPB-O#VdfG=sDkg@eJO`$H(^;2VvpQhbPNb-ldnuv?Eci=7~uo|E9={Dh7Ll z4g6)YjwaIvavso{0$K!#vN3TH?-BkZok;h7(7cOyXIY0r-c6WjV1~S#Y(9CXnIQ5` zv-!G!!=QP2^c=vW&(9w+qR*{a+!>l7*;@fiLa)*KwuaK#bN z>>Vz~;Mq}xW}$Nf{FNIpUpLoKZ-YK))6>ugYlRbsHz$z_;c&;nl>rSFAvgt&eHLW&li=;uDLeD+bqoj=W+? zw%MxbY=P6-ysBv<9zhR@6?gz>IeCt)g1L0q!q`f2ZeiV|n?{{s#5tpe(w4TgrGqGA zV?x;moh)NGo!OXEemSTVO6ONT=u9;8ukd+J@1-~0x=46)&UxPFectz;^PW?asn(?b zwVT>aFPjQ=b#?#8M!^f7dfX(sivE9dGa=XyfYPuq;3(E9%#)ZWZH4tA%!gqStmE({ zDtzS9Cyo|d&|DOh($=N zfLWs;q!1g^V$E+~b_eo}41H=*~@1ON+7lR)_Ymlbv9nCgqdaO^lF2oFC+)T@rp|R^zO$HhM7xsf*gS{q41`dTeP27G<6<;P)CM1G<^DTMG_NO7XN(@h9gt% zzz&a1?imyH`a3WLVOAvCoKLUaNIY51V6A0jOK>-YU7ie6aT{1Yg@ykiPe;5n?Z&|J zSbuH6_a@~2Seo2k7)gH?xH5R=lOAv98?V7vk`5!@*lpCaBKobf3Qp@lk26p()>(U| z``~oq?0E3Q^QV3DW1nZ!=ho+v_a+BkC}@FT*SP!O7s|0){CxemtWQ=lnRxF?HlDvd z@-#Q|_oJ&;2w}kDg^D#$LGz!GA1yD?N}eq=4CDFm?j;?{mcB*iwm#+9O?H87usDh9Wdk$TVT={OZ;m2o0>P@q2xVkuS3~va`_Be`V%Yq ziY+Xlti@W4&2WL*EO#Z(Tg^)%-()1v`(*?a-b?Xy;W*r+X@ylXzXFp#2A27)KCtj# z>JOG}};Q1fMuURp2XdNfGbs z-+~#35vl@z0~gwHOB|MLKF)-yz>{!AOYAu?qcB2M;3>Fd*B)vio9{m%n%LL$Gjw=+ zL`Ta_cx2PsmNqoG491u8A=^UVfsYQQ_cfD^{}ML{z6D(SXwK1|2dt?wD1snyzyDRB zylsT0Vc{~&q=fe1#r{_S>F`y!LS&aG@h9@08gO}oz@4>q0w)?9(Vzbe$%?( zB{cfKX(`Uv@bM1wMH=(oeE6tU(D*Nl{WtA1=cu+w`?Y^j@1r-0-ZFagXf_J;w~IcP zuFuh|McNKAO~gL1lUv}savOY8PQh1Y{_CR5**z&|;G;6<_n_KOfX?1NvEpbbWiB&o~n~- zf4%C}X?-_MbIiD7-k@s#h2Aj6_zAk)S6!jka@Z)C&!;e-xAVsMd)P3TYaU>i!Cdor zfX!ip&b$TeBG~V{k7O(6^*d|-o4L2{e?G+Q#r@{qWcHFB*y#+G{n@=eC-aOvkL>;y zHi6~2O<^OL?`_X*hVR2X` + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include + +#include "Magnum/Shaders/Generic.h" + +namespace Magnum { namespace Shaders { namespace Test { namespace { + +struct GenericTest: TestSuite::Tester { + explicit GenericTest(); + + template void uniformSize(); + + void projectionUniform2DConstructDefault(); + void projectionUniform2DConstructNoInit(); + void projectionUniform2DSetters(); + + void projectionUniform3DConstructDefault(); + void projectionUniform3DConstructNoInit(); + void projectionUniform3DSetters(); + + void transformationUniform2DConstructDefault(); + void transformationUniform2DConstructNoInit(); + void transformationUniform2DSetters(); + + void transformationUniform3DConstructDefault(); + void transformationUniform3DConstructNoInit(); + void transformationUniform3DSetters(); + + void textureTransformationUniformConstructDefault(); + void textureTransformationUniformConstructNoInit(); + void textureTransformationUniformSetters(); +}; + +GenericTest::GenericTest() { + addTests({&GenericTest::uniformSize, + &GenericTest::uniformSize, + &GenericTest::uniformSize, + &GenericTest::uniformSize, + &GenericTest::uniformSize, + + &GenericTest::projectionUniform2DConstructDefault, + &GenericTest::projectionUniform2DConstructNoInit, + &GenericTest::projectionUniform2DSetters, + + &GenericTest::projectionUniform3DConstructDefault, + &GenericTest::projectionUniform3DConstructNoInit, + &GenericTest::projectionUniform3DSetters, + + &GenericTest::transformationUniform2DConstructDefault, + &GenericTest::transformationUniform2DConstructNoInit, + &GenericTest::transformationUniform2DSetters, + + &GenericTest::transformationUniform3DConstructDefault, + &GenericTest::transformationUniform3DConstructNoInit, + &GenericTest::transformationUniform3DSetters, + + &GenericTest::textureTransformationUniformConstructDefault, + &GenericTest::textureTransformationUniformConstructNoInit, + &GenericTest::textureTransformationUniformSetters}); +} + +using namespace Math::Literals; + +template struct UniformTraits; +template<> struct UniformTraits { + static const char* name() { return "ProjectionUniform2D"; } +}; +template<> struct UniformTraits { + static const char* name() { return "ProjectionUniform3D"; } +}; +template<> struct UniformTraits { + static const char* name() { return "TransformationUniform2D"; } +}; +template<> struct UniformTraits { + static const char* name() { return "TransformationUniform3D"; } +}; +template<> struct UniformTraits { + static const char* name() { return "TextureTransformationUniform"; } +}; + +template void GenericTest::uniformSize() { + setTestCaseTemplateName(UniformTraits::name()); + + CORRADE_FAIL_IF(sizeof(T) % sizeof(Vector4) != 0, sizeof(T) << "is not a multiple of vec4 for UBO alignment."); + + /* 48-byte structures are fine, we'll align them to 768 bytes and not + 256, but warn about that */ + CORRADE_FAIL_IF(768 % sizeof(T) != 0, sizeof(T) << "can't fit exactly into 768-byte UBO alignment."); + if(256 % sizeof(T) != 0) + CORRADE_WARN(sizeof(T) << "can't fit exactly into 256-byte UBO alignment, only 768."); +} + +void GenericTest::projectionUniform2DConstructDefault() { + ProjectionUniform2D a; + ProjectionUniform2D b{DefaultInit}; + CORRADE_COMPARE(a.projectionMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); + CORRADE_COMPARE(b.projectionMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); + + constexpr ProjectionUniform2D ca; + constexpr ProjectionUniform2D cb{DefaultInit}; + CORRADE_COMPARE(ca.projectionMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); + CORRADE_COMPARE(cb.projectionMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void GenericTest::projectionUniform2DConstructNoInit() { + ProjectionUniform2D a; + a.projectionMatrix[2] = {1.5f, 0.3f, 3.1f, 0.5f}; + + new(&a) ProjectionUniform2D{NoInit}; + CORRADE_COMPARE(a.projectionMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void GenericTest::projectionUniform2DSetters() { + ProjectionUniform2D a; + a.setProjectionMatrix(Matrix3::projection({2.5f, 3.0f})); + CORRADE_COMPARE(a.projectionMatrix, (Matrix3x4{ + Vector4{0.8f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.666667f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); +} + +void GenericTest::projectionUniform3DConstructDefault() { + ProjectionUniform3D a; + ProjectionUniform3D b{DefaultInit}; + CORRADE_COMPARE(a.projectionMatrix, (Matrix4{ + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f} + })); + CORRADE_COMPARE(b.projectionMatrix, (Matrix4{ + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f} + })); + + constexpr ProjectionUniform3D ca; + constexpr ProjectionUniform3D cb{DefaultInit}; + CORRADE_COMPARE(ca.projectionMatrix, (Matrix4{ + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f} + })); + CORRADE_COMPARE(cb.projectionMatrix, (Matrix4{ + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f} + })); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void GenericTest::projectionUniform3DConstructNoInit() { + ProjectionUniform3D a; + a.projectionMatrix[2] = {1.5f, 0.3f, 3.1f, 0.5f}; + + new(&a) ProjectionUniform3D{NoInit}; + CORRADE_COMPARE(a.projectionMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void GenericTest::projectionUniform3DSetters() { + ProjectionUniform3D a; + a.setProjectionMatrix(Matrix4::perspectiveProjection(35.0_degf, 1.5f, 0.1f, 100.0f)); + CORRADE_COMPARE(a.projectionMatrix, Matrix4::perspectiveProjection(35.0_degf, 1.5f, 0.1f, 100.0f)); +} + +void GenericTest::transformationUniform2DConstructDefault() { + TransformationUniform2D a; + TransformationUniform2D b{DefaultInit}; + CORRADE_COMPARE(a.transformationMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); + CORRADE_COMPARE(b.transformationMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); + + constexpr TransformationUniform2D ca; + constexpr TransformationUniform2D cb{DefaultInit}; + CORRADE_COMPARE(ca.transformationMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); + CORRADE_COMPARE(cb.transformationMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void GenericTest::transformationUniform2DConstructNoInit() { + TransformationUniform2D a; + a.transformationMatrix[2] = {1.5f, 0.3f, 3.1f, 0.5f}; + + new(&a) TransformationUniform2D{NoInit}; + CORRADE_COMPARE(a.transformationMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void GenericTest::transformationUniform2DSetters() { + TransformationUniform2D a; + a.setTransformationMatrix(Matrix3::translation({2.5f, 3.0f})); + CORRADE_COMPARE(a.transformationMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{2.5f, 3.0f, 1.0f, 0.0f} + })); +} + +void GenericTest::transformationUniform3DConstructDefault() { + TransformationUniform3D a; + TransformationUniform3D b{DefaultInit}; + CORRADE_COMPARE(a.transformationMatrix, (Matrix4{ + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f} + })); + CORRADE_COMPARE(b.transformationMatrix, (Matrix4{ + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f} + })); + + constexpr TransformationUniform3D ca; + constexpr TransformationUniform3D cb{DefaultInit}; + CORRADE_COMPARE(ca.transformationMatrix, (Matrix4{ + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f} + })); + CORRADE_COMPARE(cb.transformationMatrix, (Matrix4{ + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f} + })); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void GenericTest::transformationUniform3DConstructNoInit() { + TransformationUniform3D a; + a.transformationMatrix[2] = {1.5f, 0.3f, 3.1f, 0.5f}; + + new(&a) TransformationUniform3D{NoInit}; + CORRADE_COMPARE(a.transformationMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void GenericTest::transformationUniform3DSetters() { + TransformationUniform3D a; + a.setTransformationMatrix(Matrix4::translation({3.5f, 2.0f, -1.0f})); + CORRADE_COMPARE(a.transformationMatrix, Matrix4::translation({3.5f, 2.0f, -1.0f})); +} + +void GenericTest::textureTransformationUniformConstructDefault() { + TextureTransformationUniform a; + TextureTransformationUniform b{DefaultInit}; + CORRADE_COMPARE(a.rotationScaling, (Matrix2x2{ + Vector2{1.0f, 0.0f}, + Vector2{0.0f, 1.0f} + })); + CORRADE_COMPARE(b.rotationScaling, (Matrix2x2{ + Vector2{1.0f, 0.0f}, + Vector2{0.0f, 1.0f} + })); + CORRADE_COMPARE(a.offset, (Vector2{0.0f, 0.0f})); + CORRADE_COMPARE(b.offset, (Vector2{0.0f, 0.0f})); + + constexpr TextureTransformationUniform ca; + constexpr TextureTransformationUniform cb{DefaultInit}; + CORRADE_COMPARE(ca.rotationScaling, (Matrix2x2{ + Vector2{1.0f, 0.0f}, + Vector2{0.0f, 1.0f} + })); + CORRADE_COMPARE(cb.rotationScaling, (Matrix2x2{ + Vector2{1.0f, 0.0f}, + Vector2{0.0f, 1.0f} + })); + CORRADE_COMPARE(ca.offset, (Vector2{0.0f, 0.0f})); + CORRADE_COMPARE(cb.offset, (Vector2{0.0f, 0.0f})); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void GenericTest::textureTransformationUniformConstructNoInit() { + TextureTransformationUniform a; + a.rotationScaling[1] = {2.5f, -3.0f}; + a.offset = {2.7f, 0.3f}; + + new(&a) TextureTransformationUniform{NoInit}; + CORRADE_COMPARE(a.rotationScaling[1], (Vector2{2.5f, -3.0f})); + CORRADE_COMPARE(a.offset, (Vector2{2.7f, 0.3f})); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void GenericTest::textureTransformationUniformSetters() { + TextureTransformationUniform a; + a.setTextureMatrix(Matrix3::translation({2.6f, 0.3f})* + Matrix3::rotation(90.0_degf)); + CORRADE_COMPARE(a.rotationScaling, (Matrix2x2{ + Vector2{ 0.0f, 1.0f}, + Vector2{-1.0f, 0.0f} + })); + CORRADE_COMPARE(a.offset, (Vector2{2.6f, 0.3f})); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Shaders::Test::GenericTest) diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index 92f0c2f4a..717f11326 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -61,6 +61,17 @@ #include "Magnum/Trade/AbstractImporter.h" #include "Magnum/Trade/MeshData.h" +#ifndef MAGNUM_TARGET_GLES2 +#include "Magnum/GL/MeshView.h" +#include "Magnum/MeshTools/Concatenate.h" +#include "Magnum/MeshTools/GenerateIndices.h" +#include "Magnum/Primitives/Cone.h" +#include "Magnum/Primitives/Plane.h" +#include "Magnum/Primitives/Square.h" +#include "Magnum/Shaders/Generic.h" +#include "Magnum/Shaders/MeshVisualizer.h" +#endif + #include "configure.h" namespace Magnum { namespace Shaders { namespace Test { namespace { @@ -69,14 +80,38 @@ struct MeshVisualizerGLTest: GL::OpenGLTester { explicit MeshVisualizerGLTest(); void construct2D(); + #ifndef MAGNUM_TARGET_GLES2 + void constructUniformBuffers2D(); + #endif void construct3D(); + #ifndef MAGNUM_TARGET_GLES2 + void constructUniformBuffers3D(); + #endif void construct2DInvalid(); + #ifndef MAGNUM_TARGET_GLES2 + void constructUniformBuffers2DInvalid(); + #endif void construct3DInvalid(); + #ifndef MAGNUM_TARGET_GLES2 + void constructUniformBuffers3DInvalid(); + #endif void constructMove2D(); + #ifndef MAGNUM_TARGET_GLES2 + void constructMoveUniformBuffers2D(); + #endif void constructMove3D(); + #ifndef MAGNUM_TARGET_GLES2 + void constructMoveUniformBuffers3D(); + #endif + #ifndef MAGNUM_TARGET_GLES2 + void setUniformUniformBuffersEnabled2D(); + void setUniformUniformBuffersEnabled3D(); + void bindBufferUniformBuffersNotEnabled2D(); + void bindBufferUniformBuffersNotEnabled3D(); + #endif void setWireframeNotEnabled2D(); void setWireframeNotEnabled3D(); #ifndef MAGNUM_TARGET_GLES2 @@ -86,34 +121,44 @@ struct MeshVisualizerGLTest: GL::OpenGLTester { #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) void setTangentBitangentNormalNotEnabled3D(); #endif + #ifndef MAGNUM_TARGET_GLES2 + void setWrongDrawOffset2D(); + void setWrongDrawOffset3D(); + #endif void renderSetup(); void renderTeardown(); #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - void renderDefaultsWireframe2D(); - void renderDefaultsWireframe3D(); + template void renderDefaultsWireframe2D(); + template void renderDefaultsWireframe3D(); #endif #ifndef MAGNUM_TARGET_GLES2 - void renderDefaultsObjectId2D(); - void renderDefaultsObjectId3D(); - void renderDefaultsVertexId2D(); - void renderDefaultsVertexId3D(); - void renderDefaultsPrimitiveId2D(); - void renderDefaultsPrimitiveId3D(); + template void renderDefaultsObjectId2D(); + template void renderDefaultsObjectId3D(); + template void renderDefaultsVertexId2D(); + template void renderDefaultsVertexId3D(); + template void renderDefaultsPrimitiveId2D(); + template void renderDefaultsPrimitiveId3D(); #endif #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - void renderDefaultsTangentBitangentNormal(); + template void renderDefaultsTangentBitangentNormal(); #endif - void renderWireframe2D(); - void renderWireframe3D(); + template void renderWireframe2D(); + template void renderWireframe3D(); #ifndef MAGNUM_TARGET_GLES2 - void renderObjectVertexPrimitiveId2D(); - void renderObjectVertexPrimitiveId3D(); + template void renderObjectVertexPrimitiveId2D(); + template void renderObjectVertexPrimitiveId3D(); #endif #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + /* This tests something that's irrelevant to UBOs */ void renderWireframe3DPerspective(); - void renderTangentBitangentNormal(); + template void renderTangentBitangentNormal(); + #endif + + #ifndef MAGNUM_TARGET_GLES2 + void renderMulti2D(); + void renderMulti3D(); #endif private: @@ -146,6 +191,8 @@ constexpr struct { const char* name; MeshVisualizerGL2D::Flags flags; } ConstructData2D[] { + /* Whatever is added here should probably go also into + ConstructUniformBuffersData2D */ #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) {"wireframe", MeshVisualizerGL2D::Flag::Wireframe}, #endif @@ -160,10 +207,36 @@ constexpr struct { #endif }; +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + MeshVisualizerGL2D::Flags flags; + UnsignedInt materialCount, drawCount; +} ConstructUniformBuffersData2D[] { + {"classic fallback", MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 1, 1}, + {"", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 1, 1}, + {"multiple materials, draws", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 64, 128}, + /* The rest is basically a copy of ConstructData2D with UniformBuffers + added */ + #ifndef MAGNUM_TARGET_WEBGL + {"wireframe", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe, 1, 1}, + #endif + {"wireframe w/o GS", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 1, 1}, + {"object ID", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::InstancedObjectId, 1, 1}, + {"vertex ID", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::VertexId, 1, 1}, + #ifndef MAGNUM_TARGET_WEBGL + {"primitive ID", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::PrimitiveId, 1, 1}, + #endif + {"primitive ID from vertex ID", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::PrimitiveIdFromVertexId, 1, 1} +}; +#endif + constexpr struct { const char* name; MeshVisualizerGL3D::Flags flags; } ConstructData3D[] { + /* Whatever is added here should probably go also into + ConstructUniformBuffersData3D */ #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) {"wireframe", MeshVisualizerGL3D::Flag::Wireframe}, #endif @@ -190,6 +263,42 @@ constexpr struct { #endif }; +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + MeshVisualizerGL3D::Flags flags; + UnsignedInt materialCount, drawCount; +} ConstructUniformBuffersData3D[] { + {"classic fallback", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 1, 1}, + {"", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 1, 1}, + {"multiple materials, draws", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 64, 128}, + /* The rest is basically a copy of ConstructData2D with UniformBuffers + added */ + #ifndef MAGNUM_TARGET_WEBGL + {"wireframe", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe, 1, 1}, + #endif + {"wireframe w/o GS", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 1, 1}, + {"object ID", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::InstancedObjectId, 1, 1}, + {"vertex ID", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::VertexId, 1, 1}, + #ifndef MAGNUM_TARGET_WEBGL + {"primitive ID", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::PrimitiveId, 1, 1}, + #endif + {"primitive ID from vertex ID", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId, 1, 1}, + #ifndef MAGNUM_TARGET_WEBGL + {"tangent direction", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::TangentDirection, 1, 1}, + {"bitangent direction from tangent", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::BitangentFromTangentDirection, 1, 1}, + {"bitangent direction", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::BitangentDirection, 1, 1}, + {"normal direction", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::NormalDirection, 1, 1}, + {"tbn direction", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentDirection|MeshVisualizerGL3D::Flag::NormalDirection, 1, 1}, + {"tbn direction with bitangent from tangent", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentFromTangentDirection|MeshVisualizerGL3D::Flag::NormalDirection, 1, 1}, + {"wireframe + vertex id", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::VertexId, 1, 1}, + {"wireframe + t/n direction", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::NormalDirection, 1, 1}, + {"wireframe + object id + t/n direction", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::NormalDirection, 1, 1}, + {"wireframe + vertex id + t/b direction", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::VertexId|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentDirection, 1, 1} + #endif +}; +#endif + constexpr struct { const char* name; MeshVisualizerGL2D::Flags flags; @@ -213,6 +322,20 @@ constexpr struct { #endif }; +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + MeshVisualizerGL2D::Flags flags; + UnsignedInt materialCount, drawCount; + const char* message; +} ConstructUniformBuffersInvalidData2D[] { + {"zero draws", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 1, 0, + "draw count can't be zero"}, + {"zero materials", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 0, 1, + "material count can't be zero"} +}; +#endif + constexpr struct { const char* name; MeshVisualizerGL3D::Flags flags; @@ -244,6 +367,20 @@ constexpr struct { #endif }; +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + MeshVisualizerGL3D::Flags flags; + UnsignedInt materialCount, drawCount; + const char* message; +} ConstructUniformBuffersInvalidData3D[] { + {"zero draws", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 1, 0, + "draw count can't be zero"}, + {"zero materials", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 0, 1, + "material count can't be zero"}, +}; +#endif + #ifndef MAGNUM_TARGET_GLES2 constexpr struct { const char* name; @@ -401,42 +538,168 @@ constexpr struct { }; #endif +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + const char* expected; + MeshVisualizerGL2D::Flags flags; + UnsignedInt materialCount, drawCount; + UnsignedInt uniformIncrement; + Float maxThreshold, meanThreshold; +} RenderMultiData2D[] { + #ifndef MAGNUM_TARGET_WEBGL + {"bind with offset, wireframe", "multidraw-wireframe2D.tga", + MeshVisualizerGL2D::Flag::Wireframe, + 1, 1, 16, 0.0f, 0.0f}, + #endif + {"bind with offset, w/o GS", "multidraw-wireframe-nogeo2D.tga", + MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, + 1, 1, 16, 0.0f, 0.0f}, + {"bind with offset, vertex ID", "multidraw-vertexid2D.tga", + MeshVisualizerGL2D::Flag::VertexId, + 1, 1, 16, 0.0f, 0.0f}, + #ifndef MAGNUM_TARGET_WEBGL + {"draw offset, wireframe", "multidraw-wireframe2D.tga", + MeshVisualizerGL2D::Flag::Wireframe, + 2, 3, 1, 0.0f, 0.0f}, + #endif + {"draw offset, wireframe w/o GS", "multidraw-wireframe-nogeo2D.tga", + MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, + 2, 3, 1, 0.0f, 0.0f}, + {"draw offset, vertex ID", "multidraw-vertexid2D.tga", + MeshVisualizerGL2D::Flag::VertexId, + 2, 3, 1, 0.0f, 0.0f} +}; + +constexpr struct { + const char* name; + const char* expected; + MeshVisualizerGL3D::Flags flags; + UnsignedInt materialCount, drawCount; + UnsignedInt uniformIncrement; + Float maxThreshold, meanThreshold; +} RenderMultiData3D[] { + #ifndef MAGNUM_TARGET_WEBGL + {"bind with offset, wireframe", "multidraw-wireframe3D.tga", + MeshVisualizerGL3D::Flag::Wireframe, + 1, 1, 16, 0.0f, 0.0f}, + {"bind with offset, wireframe + TBN", "multidraw-wireframe-tbn3D.tga", + MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentFromTangentDirection|MeshVisualizerGL3D::Flag::NormalDirection, + 1, 1, 16, 0.0f, 0.0f}, + #endif + {"bind with offset, wireframe w/o GS", "multidraw-wireframe-nogeo3D.tga", + MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, + 1, 1, 16, 0.0f, 0.0f}, + {"bind with offset, vertex ID", "multidraw-vertexid3D.tga", + MeshVisualizerGL3D::Flag::VertexId, + 1, 1, 16, 0.0f, 0.0f}, + #ifndef MAGNUM_TARGET_WEBGL + {"draw offset, wireframe", "multidraw-wireframe3D.tga", + MeshVisualizerGL3D::Flag::Wireframe, + 2, 3, 1, 0.0f, 0.0f}, + {"draw offset, wireframe + TBN", "multidraw-wireframe-tbn3D.tga", + MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentFromTangentDirection|MeshVisualizerGL3D::Flag::NormalDirection, + 2, 3, 1, 0.0f, 0.0f}, + #endif + {"draw offset, wireframe w/o GS", "multidraw-wireframe-nogeo3D.tga", + MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, + 2, 3, 1, 0.0f, 0.0f}, + {"draw offset, vertex ID", "multidraw-vertexid3D.tga", + MeshVisualizerGL3D::Flag::VertexId, + 2, 3, 1, 0.0f, 0.0f} +}; +#endif + MeshVisualizerGLTest::MeshVisualizerGLTest() { addInstancedTests({&MeshVisualizerGLTest::construct2D}, Containers::arraySize(ConstructData2D)); + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({&MeshVisualizerGLTest::constructUniformBuffers2D}, + Containers::arraySize(ConstructUniformBuffersData2D)); + #endif + addInstancedTests({&MeshVisualizerGLTest::construct3D}, Containers::arraySize(ConstructData3D)); + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({&MeshVisualizerGLTest::constructUniformBuffers3D}, + Containers::arraySize(ConstructUniformBuffersData3D)); + #endif + addInstancedTests({&MeshVisualizerGLTest::construct2DInvalid}, Containers::arraySize(ConstructInvalidData2D)); + + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({&MeshVisualizerGLTest::constructUniformBuffers2DInvalid}, + Containers::arraySize(ConstructUniformBuffersInvalidData2D)); + #endif + addInstancedTests({&MeshVisualizerGLTest::construct3DInvalid}, Containers::arraySize(ConstructInvalidData3D)); - addTests({&MeshVisualizerGLTest::constructMove2D, - &MeshVisualizerGLTest::constructMove3D, + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({&MeshVisualizerGLTest::constructUniformBuffers3DInvalid}, + Containers::arraySize(ConstructUniformBuffersInvalidData3D)); + #endif + + addTests({ + &MeshVisualizerGLTest::constructMove2D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::constructMoveUniformBuffers2D, + #endif + &MeshVisualizerGLTest::constructMove3D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::constructMoveUniformBuffers3D, + #endif - &MeshVisualizerGLTest::setWireframeNotEnabled2D, - &MeshVisualizerGLTest::setWireframeNotEnabled3D, - #ifndef MAGNUM_TARGET_GLES2 - &MeshVisualizerGLTest::setColorMapNotEnabled2D, - &MeshVisualizerGLTest::setColorMapNotEnabled3D, - #endif - #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - &MeshVisualizerGLTest::setTangentBitangentNormalNotEnabled3D, - #endif - }); + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::setUniformUniformBuffersEnabled2D, + &MeshVisualizerGLTest::setUniformUniformBuffersEnabled3D, + &MeshVisualizerGLTest::bindBufferUniformBuffersNotEnabled2D, + &MeshVisualizerGLTest::bindBufferUniformBuffersNotEnabled3D, + #endif + &MeshVisualizerGLTest::setWireframeNotEnabled2D, + &MeshVisualizerGLTest::setWireframeNotEnabled3D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::setColorMapNotEnabled2D, + &MeshVisualizerGLTest::setColorMapNotEnabled3D, + #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + &MeshVisualizerGLTest::setTangentBitangentNormalNotEnabled3D, + #endif + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::setWrongDrawOffset2D, + &MeshVisualizerGLTest::setWrongDrawOffset3D + #endif + }); #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - addTests({&MeshVisualizerGLTest::renderDefaultsWireframe2D, - &MeshVisualizerGLTest::renderDefaultsWireframe3D}, + addTests({ + &MeshVisualizerGLTest::renderDefaultsWireframe2D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::renderDefaultsWireframe2D, + #endif + &MeshVisualizerGLTest::renderDefaultsWireframe3D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::renderDefaultsWireframe3D, + #endif + }, &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); #endif #ifndef MAGNUM_TARGET_GLES2 - addInstancedTests({&MeshVisualizerGLTest::renderDefaultsObjectId2D, - &MeshVisualizerGLTest::renderDefaultsObjectId3D}, + addInstancedTests({ + &MeshVisualizerGLTest::renderDefaultsObjectId2D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::renderDefaultsObjectId2D, + #endif + &MeshVisualizerGLTest::renderDefaultsObjectId3D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::renderDefaultsObjectId3D, + #endif + }, Containers::arraySize(ObjectIdDefaultsData), &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); @@ -445,30 +708,63 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { #ifndef MAGNUM_TARGET_GLES2 addTests({ &MeshVisualizerGLTest::renderDefaultsVertexId2D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::renderDefaultsVertexId2D, + #endif &MeshVisualizerGLTest::renderDefaultsVertexId3D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::renderDefaultsVertexId3D, + #endif &MeshVisualizerGLTest::renderDefaultsPrimitiveId2D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::renderDefaultsPrimitiveId2D, + #endif &MeshVisualizerGLTest::renderDefaultsPrimitiveId3D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::renderDefaultsPrimitiveId3D, + #endif #ifndef MAGNUM_TARGET_WEBGL - &MeshVisualizerGLTest::renderDefaultsTangentBitangentNormal + &MeshVisualizerGLTest::renderDefaultsTangentBitangentNormal, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::renderDefaultsTangentBitangentNormal, + #endif #endif }, &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); #endif - addInstancedTests({&MeshVisualizerGLTest::renderWireframe2D}, + addInstancedTests({ + &MeshVisualizerGLTest::renderWireframe2D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::renderWireframe2D, + #endif + }, Containers::arraySize(WireframeData2D), &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); - addInstancedTests({&MeshVisualizerGLTest::renderWireframe3D}, + addInstancedTests({ + &MeshVisualizerGLTest::renderWireframe3D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::renderWireframe3D, + #endif + }, Containers::arraySize(WireframeData3D), &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); #ifndef MAGNUM_TARGET_GLES2 - addInstancedTests({&MeshVisualizerGLTest::renderObjectVertexPrimitiveId2D, - &MeshVisualizerGLTest::renderObjectVertexPrimitiveId3D}, + addInstancedTests({ + &MeshVisualizerGLTest::renderObjectVertexPrimitiveId2D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::renderObjectVertexPrimitiveId2D, + #endif + &MeshVisualizerGLTest::renderObjectVertexPrimitiveId3D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::renderObjectVertexPrimitiveId3D, + #endif + }, Containers::arraySize(ObjectVertexPrimitiveIdData), &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); @@ -479,12 +775,29 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); - addInstancedTests({&MeshVisualizerGLTest::renderTangentBitangentNormal}, + addInstancedTests({ + &MeshVisualizerGLTest::renderTangentBitangentNormal, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGLTest::renderTangentBitangentNormal, + #endif + }, Containers::arraySize(TangentBitangentNormalData), &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); #endif + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({&MeshVisualizerGLTest::renderMulti2D}, + Containers::arraySize(RenderMultiData2D), + &MeshVisualizerGLTest::renderSetup, + &MeshVisualizerGLTest::renderTeardown); + + addInstancedTests({&MeshVisualizerGLTest::renderMulti3D}, + Containers::arraySize(RenderMultiData3D), + &MeshVisualizerGLTest::renderSetup, + &MeshVisualizerGLTest::renderTeardown); + #endif + /* 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 @@ -582,6 +895,72 @@ void MeshVisualizerGLTest::construct2D() { MAGNUM_VERIFY_NO_GL_ERROR(); } +#ifndef MAGNUM_TARGET_GLES2 +void MeshVisualizerGLTest::constructUniformBuffers2D() { + auto&& data = ConstructUniformBuffersData2D[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if((data.flags & MeshVisualizerGL2D::Flag::InstancedObjectId) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); + #endif + + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= MeshVisualizerGL2D::Flag::PrimitiveIdFromVertexId && + #ifndef MAGNUM_TARGET_GLES + !GL::Context::current().isVersionSupported(GL::Version::GL300) + #else + !GL::Context::current().isVersionSupported(GL::Version::GLES300) + #endif + ) CORRADE_SKIP("gl_VertexID not supported."); + #endif + + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags & MeshVisualizerGL2D::Flag::PrimitiveId && !(data.flags >= MeshVisualizerGL2D::Flag::PrimitiveIdFromVertexId) && + #ifndef MAGNUM_TARGET_GLES + !GL::Context::current().isVersionSupported(GL::Version::GL320) + #else + !GL::Context::current().isVersionSupported(GL::Version::GLES320) + #endif + ) CORRADE_SKIP("gl_PrimitiveID not supported."); + #endif + + #ifndef MAGNUM_TARGET_WEBGL + if((data.flags & MeshVisualizerGL2D::Flag::Wireframe) && !(data.flags & MeshVisualizerGL2D::Flag::NoGeometryShader)) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() << "is not supported."); + #endif + + #ifdef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + CORRADE_INFO("Using" << GL::Extensions::NV::shader_noperspective_interpolation::string()); + #endif + } + #endif + + #ifndef MAGNUM_TARGET_GLES + if(data.flags & MeshVisualizerGL2D::Flag::UniformBuffers && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + MeshVisualizerGL2D shader{data.flags, data.materialCount, data.drawCount}; + CORRADE_COMPARE(shader.flags(), data.flags); + CORRADE_VERIFY(shader.id()); + { + #ifdef CORRADE_TARGET_APPLE + CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); + #endif + CORRADE_VERIFY(shader.validate().first); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); +} +#endif + void MeshVisualizerGLTest::construct3D() { auto&& data = ConstructData3D[testCaseInstanceId()]; setTestCaseDescription(data.name); @@ -641,6 +1020,72 @@ void MeshVisualizerGLTest::construct3D() { MAGNUM_VERIFY_NO_GL_ERROR(); } +#ifndef MAGNUM_TARGET_GLES2 +void MeshVisualizerGLTest::constructUniformBuffers3D() { + auto&& data = ConstructUniformBuffersData3D[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if((data.flags & MeshVisualizerGL3D::Flag::InstancedObjectId) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); + #endif + + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags >= MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId && + #ifndef MAGNUM_TARGET_GLES + !GL::Context::current().isVersionSupported(GL::Version::GL300) + #else + !GL::Context::current().isVersionSupported(GL::Version::GLES300) + #endif + ) CORRADE_SKIP("gl_VertexID not supported."); + #endif + + #ifndef MAGNUM_TARGET_WEBGL + if(data.flags & MeshVisualizerGL3D::Flag::PrimitiveId && !(data.flags >= MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId) && + #ifndef MAGNUM_TARGET_GLES + !GL::Context::current().isVersionSupported(GL::Version::GL320) + #else + !GL::Context::current().isVersionSupported(GL::Version::GLES320) + #endif + ) CORRADE_SKIP("gl_PrimitiveID not supported."); + #endif + + #ifndef MAGNUM_TARGET_WEBGL + if(((data.flags & MeshVisualizerGL3D::Flag::Wireframe) && !(data.flags & MeshVisualizerGL3D::Flag::NoGeometryShader)) || (data.flags & (MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentDirection|MeshVisualizerGL3D::Flag::BitangentFromTangentDirection|MeshVisualizerGL3D::Flag::NormalDirection))) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() << "is not supported."); + #endif + + #ifdef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + CORRADE_INFO("Using" << GL::Extensions::NV::shader_noperspective_interpolation::string()); + #endif + } + #endif + + #ifndef MAGNUM_TARGET_GLES + if(data.flags & MeshVisualizerGL3D::Flag::UniformBuffers && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + MeshVisualizerGL3D shader{data.flags, data.materialCount, data.drawCount}; + CORRADE_COMPARE(shader.flags(), data.flags); + CORRADE_VERIFY(shader.id()); + { + #ifdef CORRADE_TARGET_APPLE + CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); + #endif + CORRADE_VERIFY(shader.validate().first); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); +} +#endif + void MeshVisualizerGLTest::construct2DInvalid() { auto&& data = ConstructInvalidData2D[testCaseInstanceId()]; setTestCaseDescription(data.name); @@ -655,6 +1100,27 @@ void MeshVisualizerGLTest::construct2DInvalid() { CORRADE_COMPARE(out.str(), Utility::formatString("Shaders::MeshVisualizerGL{}\n", data.message)); } +#ifndef MAGNUM_TARGET_GLES2 +void MeshVisualizerGLTest::constructUniformBuffers2DInvalid() { + auto&& data = ConstructUniformBuffersInvalidData2D[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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + MeshVisualizerGL2D{data.flags, data.materialCount, data.drawCount}; + CORRADE_COMPARE(out.str(), Utility::formatString("Shaders::MeshVisualizerGL2D: {}\n", data.message)); +} +#endif + void MeshVisualizerGLTest::construct3DInvalid() { auto&& data = ConstructInvalidData3D[testCaseInstanceId()]; setTestCaseDescription(data.name); @@ -669,6 +1135,27 @@ void MeshVisualizerGLTest::construct3DInvalid() { CORRADE_COMPARE(out.str(), Utility::formatString("Shaders::MeshVisualizerGL{}\n", data.message)); } +#ifndef MAGNUM_TARGET_GLES2 +void MeshVisualizerGLTest::constructUniformBuffers3DInvalid() { + auto&& data = ConstructUniformBuffersInvalidData3D[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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + MeshVisualizerGL3D{data.flags, data.materialCount, data.drawCount}; + CORRADE_COMPARE(out.str(), Utility::formatString("Shaders::MeshVisualizerGL3D: {}\n", data.message)); +} +#endif + void MeshVisualizerGLTest::constructMove2D() { MeshVisualizerGL2D a{MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader}; const GLuint id = a.id(); @@ -688,24 +1175,219 @@ void MeshVisualizerGLTest::constructMove2D() { CORRADE_VERIFY(!b.id()); } -void MeshVisualizerGLTest::constructMove3D() { - MeshVisualizerGL3D a{MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader}; - const GLuint id = a.id(); - CORRADE_VERIFY(id); +#ifndef MAGNUM_TARGET_GLES2 +void MeshVisualizerGLTest::constructMoveUniformBuffers2D() { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + MeshVisualizerGL2D a{MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 2, 5}; + const GLuint id = a.id(); + CORRADE_VERIFY(id); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + MeshVisualizerGL2D b{std::move(a)}; + CORRADE_COMPARE(b.id(), id); + CORRADE_COMPARE(b.flags(), MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader); + CORRADE_COMPARE(b.materialCount(), 2); + CORRADE_COMPARE(b.drawCount(), 5); + CORRADE_VERIFY(!a.id()); + + MeshVisualizerGL2D c{NoCreate}; + c = std::move(b); + CORRADE_COMPARE(c.id(), id); + CORRADE_COMPARE(c.flags(), MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader); + CORRADE_COMPARE(c.materialCount(), 2); + CORRADE_COMPARE(c.drawCount(), 5); + CORRADE_VERIFY(!b.id()); +} +#endif + +void MeshVisualizerGLTest::constructMove3D() { + MeshVisualizerGL3D a{MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader}; + const GLuint id = a.id(); + CORRADE_VERIFY(id); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + MeshVisualizerGL3D b{std::move(a)}; + CORRADE_COMPARE(b.id(), id); + CORRADE_COMPARE(b.flags(), MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader); + CORRADE_VERIFY(!a.id()); + + MeshVisualizerGL3D c{NoCreate}; + c = std::move(b); + CORRADE_COMPARE(c.id(), id); + CORRADE_COMPARE(c.flags(), MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader); + CORRADE_VERIFY(!b.id()); +} + +#ifndef MAGNUM_TARGET_GLES2 +void MeshVisualizerGLTest::constructMoveUniformBuffers3D() { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + MeshVisualizerGL3D a{MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 2, 5}; + const GLuint id = a.id(); + CORRADE_VERIFY(id); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + MeshVisualizerGL3D b{std::move(a)}; + CORRADE_COMPARE(b.id(), id); + CORRADE_COMPARE(b.flags(), MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader); + CORRADE_COMPARE(b.materialCount(), 2); + CORRADE_COMPARE(b.drawCount(), 5); + CORRADE_VERIFY(!a.id()); + + MeshVisualizerGL3D c{NoCreate}; + c = std::move(b); + CORRADE_COMPARE(c.id(), id); + CORRADE_COMPARE(c.flags(), MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader); + CORRADE_COMPARE(c.materialCount(), 2); + CORRADE_COMPARE(c.drawCount(), 5); + CORRADE_VERIFY(!b.id()); +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +void MeshVisualizerGLTest::setUniformUniformBuffersEnabled2D() { + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + MeshVisualizerGL2D shader{MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader}; + shader.setTransformationProjectionMatrix({}) + /* setViewportSize() works on both UBOs and classic */ + .setColor({}) + .setWireframeColor({}) + .setWireframeWidth({}) + .setColorMapTransformation({}, {}) + .setSmoothness({}); + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizerGL2D::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::setColor(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::setWireframeColor(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::setWireframeWidth(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::setColorMapTransformation(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL2D::setSmoothness(): the shader was created with uniform buffers enabled\n"); +} + +void MeshVisualizerGLTest::setUniformUniformBuffersEnabled3D() { + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + MeshVisualizerGL3D shader{MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader}; + shader.setProjectionMatrix({}) + .setTransformationMatrix({}) + /* setViewportSize() works on both UBOs and classic */ + .setColor({}) + .setWireframeColor({}) + .setWireframeWidth({}) + .setColorMapTransformation({}, {}) + .setSmoothness({}); + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizerGL3D::setProjectionMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL3D::setTransformationMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::setColor(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::setWireframeColor(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::setWireframeWidth(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::setColorMapTransformation(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL3D::setSmoothness(): the shader was created with uniform buffers enabled\n"); + + out.str({}); + + #ifndef MAGNUM_TARGET_WEBGL + shader + .setNormalMatrix({}) + .setLineWidth({}) + .setLineLength({}); + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizerGL3D::setNormalMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL3D::setLineWidth(): the shader was created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL3D::setLineLength(): the shader was created with uniform buffers enabled\n"); + #endif +} + +void MeshVisualizerGLTest::bindBufferUniformBuffersNotEnabled2D() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + GL::Buffer buffer; + MeshVisualizerGL2D shader{MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader}; + shader.bindTransformationProjectionBuffer(buffer) + .bindTransformationProjectionBuffer(buffer, 0, 16) + .bindDrawBuffer(buffer) + .bindDrawBuffer(buffer, 0, 16) + .bindMaterialBuffer(buffer) + .bindMaterialBuffer(buffer, 0, 16) + .setDrawOffset(0); + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizerGL2D::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL2D::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL2D::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL2D::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::setDrawOffset(): the shader was not created with uniform buffers enabled\n"); +} - MAGNUM_VERIFY_NO_GL_ERROR(); +void MeshVisualizerGLTest::bindBufferUniformBuffersNotEnabled3D() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif - MeshVisualizerGL3D b{std::move(a)}; - CORRADE_COMPARE(b.id(), id); - CORRADE_COMPARE(b.flags(), MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader); - CORRADE_VERIFY(!a.id()); + std::ostringstream out; + Error redirectError{&out}; - MeshVisualizerGL3D c{NoCreate}; - c = std::move(b); - CORRADE_COMPARE(c.id(), id); - CORRADE_COMPARE(c.flags(), MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader); - CORRADE_VERIFY(!b.id()); + GL::Buffer buffer; + MeshVisualizerGL3D shader{MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader}; + shader.bindProjectionBuffer(buffer) + .bindProjectionBuffer(buffer, 0, 16) + .bindTransformationBuffer(buffer) + .bindTransformationBuffer(buffer, 0, 16) + .bindDrawBuffer(buffer) + .bindDrawBuffer(buffer, 0, 16) + .bindMaterialBuffer(buffer) + .bindMaterialBuffer(buffer, 0, 16) + .setDrawOffset(0); + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizerGL3D::bindProjectionBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL3D::bindProjectionBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL3D::bindTransformationBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL3D::bindTransformationBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL3D::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL3D::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::MeshVisualizerGL::setDrawOffset(): the shader was not created with uniform buffers enabled\n"); } +#endif void MeshVisualizerGLTest::setWireframeNotEnabled2D() { #ifdef CORRADE_NO_ASSERT @@ -843,6 +1525,44 @@ void MeshVisualizerGLTest::setTangentBitangentNormalNotEnabled3D() { } #endif +#ifndef MAGNUM_TARGET_GLES2 +void MeshVisualizerGLTest::setWrongDrawOffset2D() { + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + MeshVisualizerGL2D{MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 2, 5} + .setDrawOffset(5); + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizerGL::setDrawOffset(): draw offset 5 is out of bounds for 5 draws\n"); +} + +void MeshVisualizerGLTest::setWrongDrawOffset3D() { + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + MeshVisualizerGL3D{MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 2, 5} + .setDrawOffset(5); + CORRADE_COMPARE(out.str(), + "Shaders::MeshVisualizerGL::setDrawOffset(): draw offset 5 is out of bounds for 5 draws\n"); +} +#endif + constexpr Vector2i RenderSize{80, 80}; void MeshVisualizerGLTest::renderSetup() { @@ -880,7 +1600,16 @@ void MeshVisualizerGLTest::renderTeardown() { } #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) -void MeshVisualizerGLTest::renderDefaultsWireframe2D() { +template void MeshVisualizerGLTest::renderDefaultsWireframe2D() { + if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() << "is not supported."); @@ -896,8 +1625,26 @@ void MeshVisualizerGLTest::renderDefaultsWireframe2D() { GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(16)); - MeshVisualizerGL2D shader{MeshVisualizerGL2D::Flag::Wireframe}; - shader.draw(circle); + MeshVisualizerGL2D shader{MeshVisualizerGL2D::Flag::Wireframe|flag}; + + if(flag == MeshVisualizerGL2D::Flag{}) { + shader.draw(circle); + } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerDrawUniform2D{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerMaterialUniform{} + }}; + shader + .bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(circle); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -915,9 +1662,26 @@ void MeshVisualizerGLTest::renderDefaultsWireframe2D() { } /** @todo make this unnecessary */ - shader - .setViewportSize({80, 80}) - .draw(circle); + shader.setViewportSize({80, 80}); + + if(flag == MeshVisualizerGL2D::Flag{}) { + shader.draw(circle); + } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerDrawUniform2D{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerMaterialUniform{} + }}; + shader + .bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(circle); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -929,7 +1693,16 @@ void MeshVisualizerGLTest::renderDefaultsWireframe2D() { (DebugTools::CompareImageToFile{_manager, 1.0f, 0.082f})); } -void MeshVisualizerGLTest::renderDefaultsWireframe3D() { +template void MeshVisualizerGLTest::renderDefaultsWireframe3D() { + if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() << "is not supported."); @@ -945,8 +1718,30 @@ void MeshVisualizerGLTest::renderDefaultsWireframe3D() { GL::Mesh sphere = MeshTools::compile(Primitives::icosphereSolid(1)); - MeshVisualizerGL3D shader{MeshVisualizerGL3D::Flag::Wireframe}; - shader.draw(sphere); + MeshVisualizerGL3D shader{MeshVisualizerGL3D::Flag::Wireframe|flag}; + + if(flag == MeshVisualizerGL3D::Flag{}) { + shader.draw(sphere); + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{} + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerDrawUniform3D{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerMaterialUniform{} + }}; + shader + .bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(sphere); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -964,9 +1759,30 @@ void MeshVisualizerGLTest::renderDefaultsWireframe3D() { } /** @todo make this unnecessary */ - shader - .setViewportSize({80, 80}) - .draw(sphere); + shader.setViewportSize({80, 80}); + + if(flag == MeshVisualizerGL3D::Flag{}) { + shader.draw(sphere); + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{} + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerDrawUniform3D{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerMaterialUniform{} + }}; + shader + .bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(sphere); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -980,10 +1796,19 @@ void MeshVisualizerGLTest::renderDefaultsWireframe3D() { #endif #ifndef MAGNUM_TARGET_GLES2 -void MeshVisualizerGLTest::renderDefaultsObjectId2D() { +template void MeshVisualizerGLTest::renderDefaultsObjectId2D() { auto&& data = ObjectIdDefaultsData[testCaseInstanceId()]; setTestCaseDescription(data.name); + if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); @@ -1011,9 +1836,27 @@ void MeshVisualizerGLTest::renderDefaultsObjectId2D() { Containers::arrayView(ids)} })); - MeshVisualizerGL2D{MeshVisualizerGL2D::Flag::InstancedObjectId} - .bindColorMapTexture(colorMapTexture) - .draw(circle); + MeshVisualizerGL2D shader{MeshVisualizerGL2D::Flag::InstancedObjectId|flag}; + shader.bindColorMapTexture(colorMapTexture); + + if(flag == MeshVisualizerGL2D::Flag{}) { + shader.draw(circle); + } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerDrawUniform2D{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerMaterialUniform{} + }}; + shader + .bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(circle); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1029,10 +1872,19 @@ void MeshVisualizerGLTest::renderDefaultsObjectId2D() { (DebugTools::CompareImageToFile{_manager, 150.67f, 0.45f})); } -void MeshVisualizerGLTest::renderDefaultsObjectId3D() { +template void MeshVisualizerGLTest::renderDefaultsObjectId3D() { auto&& data = ObjectIdDefaultsData[testCaseInstanceId()]; setTestCaseDescription(data.name); + if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); @@ -1060,9 +1912,31 @@ void MeshVisualizerGLTest::renderDefaultsObjectId3D() { Containers::arrayView(ids)} })); - MeshVisualizerGL3D{MeshVisualizerGL3D::Flag::InstancedObjectId} - .bindColorMapTexture(colorMapTexture) - .draw(icosphere); + MeshVisualizerGL3D shader{MeshVisualizerGL3D::Flag::InstancedObjectId|flag}; + shader.bindColorMapTexture(colorMapTexture); + + if(flag == MeshVisualizerGL3D::Flag{}) { + shader.draw(icosphere); + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{} + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerDrawUniform3D{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerMaterialUniform{} + }}; + shader + .bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(icosphere); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1078,7 +1952,16 @@ void MeshVisualizerGLTest::renderDefaultsObjectId3D() { (DebugTools::CompareImageToFile{_manager, 150.67f, 0.165f})); } -void MeshVisualizerGLTest::renderDefaultsVertexId2D() { +template void MeshVisualizerGLTest::renderDefaultsVertexId2D() { + if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::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."); @@ -1087,9 +1970,29 @@ void MeshVisualizerGLTest::renderDefaultsVertexId2D() { if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP("gl_VertexID not supported"); - MeshVisualizerGL2D{MeshVisualizerGL2D::Flag::VertexId} - .bindColorMapTexture(_colorMapTexture) - .draw(MeshTools::compile(Primitives::circle2DSolid(16))); + GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(16)); + + MeshVisualizerGL2D shader{MeshVisualizerGL2D::Flag::VertexId|flag}; + shader.bindColorMapTexture(_colorMapTexture); + + if(flag == MeshVisualizerGL2D::Flag{}) { + shader.draw(circle); + } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerDrawUniform2D{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerMaterialUniform{} + }}; + shader + .bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(circle); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1100,7 +2003,16 @@ void MeshVisualizerGLTest::renderDefaultsVertexId2D() { (DebugTools::CompareImageToFile{_manager, 1.0f, 0.017f})); } -void MeshVisualizerGLTest::renderDefaultsVertexId3D() { +template void MeshVisualizerGLTest::renderDefaultsVertexId3D() { + if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::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."); @@ -1109,9 +2021,33 @@ void MeshVisualizerGLTest::renderDefaultsVertexId3D() { if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP("gl_VertexID not supported"); - MeshVisualizerGL3D{MeshVisualizerGL3D::Flag::VertexId} - .bindColorMapTexture(_colorMapTexture) - .draw(MeshTools::compile(Primitives::icosphereSolid(0))); + GL::Mesh icosphere = MeshTools::compile(Primitives::icosphereSolid(0)); + + MeshVisualizerGL3D shader{MeshVisualizerGL3D::Flag::VertexId|flag}; + shader.bindColorMapTexture(_colorMapTexture); + + if(flag == MeshVisualizerGL3D::Flag{}) { + shader.draw(icosphere); + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{} + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerDrawUniform3D{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerMaterialUniform{} + }}; + shader + .bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(icosphere); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1122,7 +2058,16 @@ void MeshVisualizerGLTest::renderDefaultsVertexId3D() { (DebugTools::CompareImageToFile{_manager, 1.0f, 0.012f})); } -void MeshVisualizerGLTest::renderDefaultsPrimitiveId2D() { +template void MeshVisualizerGLTest::renderDefaultsPrimitiveId2D() { + if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::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."); @@ -1160,9 +2105,29 @@ void MeshVisualizerGLTest::renderDefaultsPrimitiveId2D() { if(flags >= MeshVisualizerGL2D::Flag::PrimitiveIdFromVertexId) circleData = MeshTools::duplicate(MeshTools::generateIndices(circleData)); - MeshVisualizerGL2D{flags} - .bindColorMapTexture(_colorMapTexture) - .draw(MeshTools::compile(circleData)); + GL::Mesh circle = MeshTools::compile(circleData); + + MeshVisualizerGL2D shader{flags|flag}; + shader.bindColorMapTexture(_colorMapTexture); + + if(flag == MeshVisualizerGL2D::Flag{}) { + shader.draw(circle); + } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerDrawUniform2D{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerMaterialUniform{} + }}; + shader + .bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(circle); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1174,7 +2139,16 @@ void MeshVisualizerGLTest::renderDefaultsPrimitiveId2D() { (DebugTools::CompareImageToFile{_manager, 76.67f, 0.23f})); } -void MeshVisualizerGLTest::renderDefaultsPrimitiveId3D() { +template void MeshVisualizerGLTest::renderDefaultsPrimitiveId3D() { + if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::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."); @@ -1212,9 +2186,33 @@ void MeshVisualizerGLTest::renderDefaultsPrimitiveId3D() { if(flags >= MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId) icosphereData = MeshTools::duplicate(icosphereData); - MeshVisualizerGL3D{flags} - .bindColorMapTexture(_colorMapTexture) - .draw(MeshTools::compile(icosphereData)); + GL::Mesh icosphere = MeshTools::compile(icosphereData); + + MeshVisualizerGL3D shader{flags|flag}; + shader.bindColorMapTexture(_colorMapTexture); + + if(flag == MeshVisualizerGL3D::Flag{}) { + shader.draw(icosphere); + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{} + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerDrawUniform3D{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerMaterialUniform{} + }}; + shader + .bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(icosphere); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1228,7 +2226,16 @@ void MeshVisualizerGLTest::renderDefaultsPrimitiveId3D() { #endif #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) -void MeshVisualizerGLTest::renderDefaultsTangentBitangentNormal() { +template void MeshVisualizerGLTest::renderDefaultsTangentBitangentNormal() { + if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() << "is not supported."); @@ -1240,11 +2247,34 @@ void MeshVisualizerGLTest::renderDefaultsTangentBitangentNormal() { GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(4, 8, Primitives::UVSphereFlag::Tangents)); - MeshVisualizerGL3D{MeshVisualizerGL3D::Flag::TangentDirection| + MeshVisualizerGL3D shader{MeshVisualizerGL3D::Flag::TangentDirection| MeshVisualizerGL3D::Flag::BitangentFromTangentDirection| - MeshVisualizerGL3D::Flag::NormalDirection} - .setViewportSize({80, 80}) /** @todo make this unnecessary */ - .draw(sphere); + MeshVisualizerGL3D::Flag::NormalDirection|flag}; + /** @todo make this unnecessary */ + shader.setViewportSize({80, 80}); + + if(flag == MeshVisualizerGL3D::Flag{}) { + shader.draw(sphere); + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{} + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerDrawUniform3D{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerMaterialUniform{} + }}; + shader + .bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(sphere); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1263,10 +2293,21 @@ void MeshVisualizerGLTest::renderDefaultsTangentBitangentNormal() { } #endif -void MeshVisualizerGLTest::renderWireframe2D() { +template void MeshVisualizerGLTest::renderWireframe2D() { auto&& data = WireframeData2D[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) #ifndef MAGNUM_TARGET_GLES if(!(data.flags & MeshVisualizerGL2D::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported()) @@ -1305,14 +2346,42 @@ void MeshVisualizerGLTest::renderWireframe2D() { } } else circle = MeshTools::compile(circleData); - MeshVisualizerGL2D{data.flags|MeshVisualizerGL2D::Flag::Wireframe} - .setColor(0xffff99_rgbf) - .setWireframeColor(0x9999ff_rgbf) - .setWireframeWidth(data.width) - .setSmoothness(data.smoothness) - .setViewportSize({80, 80}) - .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) - .draw(circle); + MeshVisualizerGL2D shader{data.flags|MeshVisualizerGL2D::Flag::Wireframe|flag}; + shader.setViewportSize({80, 80}); + + if(flag == MeshVisualizerGL2D::Flag{}) { + shader + .setColor(0xffff99_rgbf) + .setWireframeColor(0x9999ff_rgbf) + .setWireframeWidth(data.width) + .setSmoothness(data.smoothness) + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + .draw(circle); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerDrawUniform2D{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerMaterialUniform{} + .setColor(0xffff99_rgbf) + .setWireframeColor(0x9999ff_rgbf) + .setWireframeWidth(data.width) + .setSmoothness(data.smoothness) + }}; + shader + .bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(circle); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1355,10 +2424,21 @@ void MeshVisualizerGLTest::renderWireframe2D() { } } -void MeshVisualizerGLTest::renderWireframe3D() { +template void MeshVisualizerGLTest::renderWireframe3D() { auto&& data = WireframeData3D[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) #ifndef MAGNUM_TARGET_GLES if(!(data.flags & MeshVisualizerGL3D::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported()) @@ -1395,18 +2475,55 @@ void MeshVisualizerGLTest::renderWireframe3D() { } } else sphere = MeshTools::compile(sphereData); - MeshVisualizerGL3D{data.flags|MeshVisualizerGL3D::Flag::Wireframe} - .setColor(0xffff99_rgbf) - .setWireframeColor(0x9999ff_rgbf) - .setWireframeWidth(data.width) - .setSmoothness(data.smoothness) - .setViewportSize({80, 80}) - .setTransformationMatrix( - Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)) - .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) - .draw(sphere); + MeshVisualizerGL3D shader{data.flags|MeshVisualizerGL3D::Flag::Wireframe|flag}; + shader.setViewportSize({80, 80}); + + if(flag == MeshVisualizerGL3D::Flag{}) { + shader + .setColor(0xffff99_rgbf) + .setWireframeColor(0x9999ff_rgbf) + .setWireframeWidth(data.width) + .setSmoothness(data.smoothness) + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)) + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + .draw(sphere); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{} + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{} + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerDrawUniform3D{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerMaterialUniform{} + .setColor(0xffff99_rgbf) + .setWireframeColor(0x9999ff_rgbf) + .setWireframeWidth(data.width) + .setSmoothness(data.smoothness) + }}; + shader + .bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1456,10 +2573,19 @@ void MeshVisualizerGLTest::renderWireframe3D() { } #ifndef MAGNUM_TARGET_GLES2 -void MeshVisualizerGLTest::renderObjectVertexPrimitiveId2D() { +template void MeshVisualizerGLTest::renderObjectVertexPrimitiveId2D() { auto&& data = ObjectVertexPrimitiveIdData[testCaseInstanceId()]; setTestCaseDescription(data.name); + if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #ifndef MAGNUM_TARGET_GLES if((data.flags2D & MeshVisualizerGL2D::Flag::InstancedObjectId) && !GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); @@ -1514,32 +2640,55 @@ void MeshVisualizerGLTest::renderObjectVertexPrimitiveId2D() { GL::Mesh circle = MeshTools::compile(circleData); - MeshVisualizerGL2D shader{data.flags2D}; + MeshVisualizerGL2D shader{data.flags2D|flag}; shader - /* Remove blue so it's clear the (wireframe) background and mapped ID - colors got mixed */ - .setColor(0xffff00_rgbf) /* Shouldn't assert (nor warn) when wireframe is not enabled */ .setViewportSize({80, 80}) - .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) .bindColorMapTexture(_colorMapTexture); - /* OTOH the wireframe color should stay at full channels, not mixed */ - if(data.flags3D & MeshVisualizerGL3D::Flag::Wireframe) - shader.setWireframeColor(0xffffff_rgbf); - - /* For vertex ID we don't want any repeat/wraparound as that causes - disruptions in the gradient and test failures. There's 17 vertices - also. */ - if(data.flags2D & MeshVisualizerGL2D::Flag::VertexId) - shader.setColorMapTransformation(1.0f, -1.0f/17.0f); - /* For object/primitive ID there's no gradient so a wraparound is okay. - This should cover the first half of the colormap, in reverse order; for - primitive ID the whole colormap due to the repeat wrapping */ - else - shader.setColorMapTransformation(0.5f, -1.0f/16.0f); - - shader.draw(circle); + if(flag == MeshVisualizerGL2D::Flag{}) { + /* Remove blue so it's clear the (wireframe) background and mapped ID + colors got mixed */ + shader.setColor(0xffff00_rgbf) + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})); + /* OTOH the wireframe color should stay at full channels, not mixed */ + if(data.flags3D & MeshVisualizerGL3D::Flag::Wireframe) + shader.setWireframeColor(0xffffff_rgbf); + /* For vertex ID we don't want any repeat/wraparound as that causes + disruptions in the gradient and test failures. There's 17 vertices + also. */ + if(data.flags2D & MeshVisualizerGL2D::Flag::VertexId) + shader.setColorMapTransformation(1.0f, -1.0f/17.0f); + /* For object/primitive ID there's no gradient so a wraparound is okay. + This should cover the first half of the colormap, in reverse order; + for primitive ID the whole colormap due to the repeat wrapping */ + else + shader.setColorMapTransformation(0.5f, -1.0f/16.0f); + shader.draw(circle); + } else if(flag == MeshVisualizerGL2D::Flag::UniformBuffers) { + /* See above for comments */ + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerDrawUniform2D{} + }}; + MeshVisualizerMaterialUniform materialUniformData[1]; + materialUniformData->setColor(0xffff00_rgbf); + if(data.flags3D & MeshVisualizerGL3D::Flag::Wireframe) + materialUniformData->setWireframeColor(0xffffff_rgbf); + if(data.flags2D & MeshVisualizerGL2D::Flag::VertexId) + materialUniformData->setColorMapTransformation(1.0f, -1.0f/17.0f); + else + materialUniformData->setColorMapTransformation(0.5f, -1.0f/16.0f); + GL::Buffer materialUniform{materialUniformData}; + shader + .bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(circle); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1556,10 +2705,19 @@ void MeshVisualizerGLTest::renderObjectVertexPrimitiveId2D() { (DebugTools::CompareImageToFile{_manager, 4.0f, 0.141f})); } -void MeshVisualizerGLTest::renderObjectVertexPrimitiveId3D() { +template void MeshVisualizerGLTest::renderObjectVertexPrimitiveId3D() { auto&& data = ObjectVertexPrimitiveIdData[testCaseInstanceId()]; setTestCaseDescription(data.name); + if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #ifndef MAGNUM_TARGET_GLES if((data.flags3D & MeshVisualizerGL3D::Flag::InstancedObjectId) && !GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); @@ -1607,37 +2765,70 @@ void MeshVisualizerGLTest::renderObjectVertexPrimitiveId3D() { data.flags3D & MeshVisualizerGL3D::Flag::NoGeometryShader) icosphereData = MeshTools::duplicate(icosphereData); - GL::Mesh circle = MeshTools::compile(icosphereData); + GL::Mesh icosphere = MeshTools::compile(icosphereData); - MeshVisualizerGL3D shader{data.flags3D}; + MeshVisualizerGL3D shader{data.flags3D|flag}; shader - /* Remove blue so it's clear the wireframe background and mapped ID - colors got mixed */ - .setColor(0xffff00_rgbf) /* Shouldn't assert (nor warn) when wireframe is not enabled */ .setViewportSize({80, 80}) - .setTransformationMatrix( - Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)) - .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) .bindColorMapTexture(_colorMapTexture); - /* OTOH the wireframe color should stay at full channels, not mixed */ - if(data.flags2D & MeshVisualizerGL2D::Flag::Wireframe) - shader.setWireframeColor(0xffffff_rgbf); - - /* For vertex ID we don't want any repeat/wraparound as that causes - disruptions in the gradient and test failures. There's 42 vertices also. */ - if(data.flags2D & MeshVisualizerGL2D::Flag::VertexId) - shader.setColorMapTransformation(1.0f, -1.0f/42.0f); - /* For object/primitive ID there's no gradient so a wraparound is okay. - This should cover the first half of the colormap, in reverse order; for - primitive ID the whole colormap due to the repeat wrapping */ - else - shader.setColorMapTransformation(0.5f, -1.0f/40.0f); - - shader.draw(circle); + if(flag == MeshVisualizerGL3D::Flag{}) { + /* Remove blue so it's clear the wireframe background and mapped ID + colors got mixed */ + shader.setColor(0xffff00_rgbf) + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)) + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)); + /* OTOH the wireframe color should stay at full channels, not mixed */ + if(data.flags2D & MeshVisualizerGL2D::Flag::Wireframe) + shader.setWireframeColor(0xffffff_rgbf); + /* For vertex ID we don't want any repeat/wraparound as that causes + disruptions in the gradient and test failures. There's 42 vertices + also. */ + if(data.flags2D & MeshVisualizerGL2D::Flag::VertexId) + shader.setColorMapTransformation(1.0f, -1.0f/42.0f); + /* For object/primitive ID there's no gradient so a wraparound is okay. + This should cover the first half of the colormap, in reverse order; + for primitive ID the whole colormap due to the repeat wrapping */ + else + shader.setColorMapTransformation(0.5f, -1.0f/40.0f); + shader.draw(icosphere); + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + /* See above for comments */ + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{} + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{} + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerDrawUniform3D{} + }}; + MeshVisualizerMaterialUniform materialUniformData[1]; + materialUniformData->setColor(0xffff00_rgbf); + if(data.flags3D & MeshVisualizerGL3D::Flag::Wireframe) + materialUniformData->setWireframeColor(0xffffff_rgbf); + if(data.flags2D & MeshVisualizerGL2D::Flag::VertexId) + materialUniformData->setColorMapTransformation(1.0f, -1.0f/42.0f); + else + materialUniformData->setColorMapTransformation(0.5f, -1.0f/40.0f); + GL::Buffer materialUniform{materialUniformData}; + shader + .bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(icosphere); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1707,10 +2898,19 @@ void MeshVisualizerGLTest::renderWireframe3DPerspective() { (DebugTools::CompareImageToFile{_manager, 0.667f, 0.002f})); } -void MeshVisualizerGLTest::renderTangentBitangentNormal() { +template void MeshVisualizerGLTest::renderTangentBitangentNormal() { auto&& data = TangentBitangentNormalData[testCaseInstanceId()]; setTestCaseDescription(data.name); + if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() << "is not supported."); @@ -1808,25 +3008,58 @@ void MeshVisualizerGLTest::renderTangentBitangentNormal() { .draw(mesh); } - MeshVisualizerGL3D shader{data.flags}; - shader - /** @todo make this unnecessary */ - .setViewportSize({80, 80}) - .setTransformationMatrix(transformation) - .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) - .setNormalMatrix(transformation.normalMatrix()*data.multiply) - .setSmoothness(data.smoothness) - .setLineLength(data.lineLength) - .setLineWidth(data.lineWidth); - - if(data.flags & MeshVisualizerGL3D::Flag::Wireframe) shader - .setColor(0xffff99_rgbf) - .setWireframeColor(0x9999ff_rgbf); - if(data.flags & MeshVisualizerGL3D::Flag::PrimitiveId) shader - .bindColorMapTexture(_colorMapTexture) - .setColorMapTransformation(1.0f/512.0f, 0.5f); + MeshVisualizerGL3D shader{data.flags|flag}; + /** @todo make this unnecessary */ + shader.setViewportSize({80, 80}); + if(data.flags & MeshVisualizerGL3D::Flag::PrimitiveId) + shader.bindColorMapTexture(_colorMapTexture); - shader.draw(mesh); + if(flag == MeshVisualizerGL3D::Flag{}) { + shader.setTransformationMatrix(transformation) + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + .setNormalMatrix(transformation.normalMatrix()*data.multiply) + .setSmoothness(data.smoothness) + .setLineLength(data.lineLength) + .setLineWidth(data.lineWidth); + if(data.flags & MeshVisualizerGL3D::Flag::Wireframe) + shader + .setColor(0xffff99_rgbf) + .setWireframeColor(0x9999ff_rgbf); + if(data.flags & MeshVisualizerGL3D::Flag::PrimitiveId) + shader.setColorMapTransformation(1.0f/512.0f, 0.5f); + shader.draw(mesh); + } else if(flag == MeshVisualizerGL3D::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{} + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{} + .setTransformationMatrix(transformation) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + MeshVisualizerDrawUniform3D{} + .setNormalMatrix(transformation.normalMatrix()*data.multiply) + }}; + MeshVisualizerMaterialUniform materialUniformData[1]; + (*materialUniformData) + .setSmoothness(data.smoothness) + .setLineLength(data.lineLength) + .setLineWidth(data.lineWidth); + if(data.flags & MeshVisualizerGL3D::Flag::Wireframe) + (*materialUniformData) + .setColor(0xffff99_rgbf) + .setWireframeColor(0x9999ff_rgbf); + if(data.flags & MeshVisualizerGL3D::Flag::PrimitiveId) + materialUniformData->setColorMapTransformation(1.0f/512.0f, 0.5f); + GL::Buffer materialUniform{materialUniformData}; + shader + .bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .draw(mesh); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1852,6 +3085,372 @@ void MeshVisualizerGLTest::renderTangentBitangentNormal() { } #endif +#ifndef MAGNUM_TARGET_GLES2 +void MeshVisualizerGLTest::renderMulti2D() { + auto&& data = RenderMultiData2D[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + #ifndef MAGNUM_TARGET_WEBGL + if((data.flags & MeshVisualizerGL2D::Flag::Wireframe) && !(data.flags & MeshVisualizerGL2D::Flag::NoGeometryShader)) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() << "is not supported."); + #endif + } + #endif + + /* Circle is a fan, plane is a strip, make it indexed first */ + Trade::MeshData circleData = MeshTools::generateIndices(Primitives::circle2DSolid(8)); + Trade::MeshData squareData = MeshTools::generateIndices(Primitives::squareSolid()); + Trade::MeshData triangleData = MeshTools::generateIndices(Primitives::circle2DSolid(3)); + /* For a GS-less wireframe we have to deindex the meshes */ + if(data.flags & MeshVisualizerGL2D::Flag::NoGeometryShader) + for(Trade::MeshData* i: {&circleData, &squareData, &triangleData}) + *i = MeshTools::duplicate(*i); + GL::Mesh mesh = MeshTools::compile(MeshTools::concatenate({circleData, squareData, triangleData})); + GL::MeshView circle{mesh}; + circle.setCount(data.flags & MeshVisualizerGL2D::Flag::NoGeometryShader ? + circleData.vertexCount() : circleData.indexCount()); + GL::MeshView square{mesh}; + square.setCount(data.flags & MeshVisualizerGL2D::Flag::NoGeometryShader ? + squareData.vertexCount() : squareData.indexCount()); + if(data.flags & MeshVisualizerGL2D::Flag::NoGeometryShader) + square.setBaseVertex(circleData.vertexCount()); + else + square.setIndexRange(circleData.indexCount()); + GL::MeshView triangle{mesh}; + triangle.setCount(data.flags & MeshVisualizerGL2D::Flag::NoGeometryShader ? + triangleData.vertexCount() : triangleData.indexCount()); + if(data.flags & MeshVisualizerGL2D::Flag::NoGeometryShader) + triangle.setBaseVertex(circleData.vertexCount() + squareData.vertexCount()); + else triangle.setIndexRange(circleData.indexCount() + squareData.indexCount()); + + /* Some drivers have uniform offset alignment as high as 256, which means + the subsequent sets of uniforms have to be aligned to a multiply of it. + The data.uniformIncrement is set high enough to ensure that, in the + non-offset-bind case this value is 1. */ + + Containers::Array materialData{data.uniformIncrement + 1}; + materialData[0*data.uniformIncrement] = MeshVisualizerMaterialUniform{} + .setColor(0xffffcc_rgbf) + .setWireframeColor(0xcc0000_rgbf) + .setColorMapTransformation(0.5f/circleData.vertexCount(), 1.0f/circleData.vertexCount()); + materialData[1*data.uniformIncrement] = MeshVisualizerMaterialUniform{} + .setColor(0xccffff_rgbf) + .setWireframeColor(0x0000cc_rgbf) + .setWireframeWidth(2.5f) + .setColorMapTransformation(0.5f/triangleData.vertexCount(), 1.0f/triangleData.vertexCount()); + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, materialData}; + + Containers::Array transformationProjectionData{2*data.uniformIncrement + 1}; + transformationProjectionData[0*data.uniformIncrement] = TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})* + Matrix3::translation({-1.25f, -1.25f}) + ); + transformationProjectionData[1*data.uniformIncrement] = TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})* + Matrix3::translation({ 1.25f, -1.25f}) + ); + transformationProjectionData[2*data.uniformIncrement] = TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})* + Matrix3::translation({ 0.00f, 1.25f}) + ); + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, transformationProjectionData}; + + Containers::Array drawData{2*data.uniformIncrement + 1}; + /* Material offsets are zero if we have single draw, as those are done with + UBO offset bindings instead. */ + drawData[0*data.uniformIncrement] = MeshVisualizerDrawUniform2D{} + .setMaterialId(data.drawCount == 1 ? 0 : 0); + drawData[1*data.uniformIncrement] = MeshVisualizerDrawUniform2D{} + .setMaterialId(data.drawCount == 1 ? 0 : 1); + drawData[2*data.uniformIncrement] = MeshVisualizerDrawUniform2D{} + .setMaterialId(data.drawCount == 1 ? 0 : 1); + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; + + MeshVisualizerGL2D shader{MeshVisualizerGL2D::Flag::UniformBuffers|data.flags, data.materialCount, data.drawCount}; + shader.setViewportSize(Vector2{RenderSize}); + if(data.flags & MeshVisualizerGL2D::Flag::VertexId) + shader.bindColorMapTexture(_colorMapTexture); + + /* Just one draw, rebinding UBOs each time */ + if(data.drawCount == 1) { + shader.bindMaterialBuffer(materialUniform, + 0*data.uniformIncrement*sizeof(MeshVisualizerMaterialUniform), + sizeof(MeshVisualizerMaterialUniform)); + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 0*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), + sizeof(TransformationProjectionUniform2D)); + shader.bindDrawBuffer(drawUniform, + 0*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform2D), + sizeof(MeshVisualizerDrawUniform2D)); + shader.draw(circle); + + shader.bindMaterialBuffer(materialUniform, + 1*data.uniformIncrement*sizeof(MeshVisualizerMaterialUniform), + sizeof(MeshVisualizerMaterialUniform)); + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 1*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), + sizeof(TransformationProjectionUniform2D)); + shader.bindDrawBuffer(drawUniform, + 1*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform2D), + sizeof(MeshVisualizerDrawUniform2D)); + shader.draw(square); + + shader.bindMaterialBuffer(materialUniform, + 1*data.uniformIncrement*sizeof(MeshVisualizerMaterialUniform), + sizeof(MeshVisualizerMaterialUniform)); + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 2*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), + sizeof(TransformationProjectionUniform2D)); + shader.bindDrawBuffer(drawUniform, + 2*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform2D), + sizeof(MeshVisualizerDrawUniform2D)); + shader.draw(triangle); + + /* Otherwise using the draw offset */ + } else { + shader.bindMaterialBuffer(materialUniform) + .bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform); + shader.setDrawOffset(0) + .draw(circle); + shader.setDrawOffset(1) + .draw(square); + shader.setDrawOffset(2) + .draw(triangle); + }; + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + /* + Wireframe case: + + - Circle should be lower left, pink with red wireframe + - Square lower right, cyan with thick blue wireframe + - Triangle up center, cyan with thick blue wireframe + + Vertex ID case: + + - Circle and triangle should have both almost the full color map + range, one tinted pink, one cyan + - Square tinted cyan, with just two colors + */ + 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, "MeshVisualizerTestFiles", data.expected}), + (DebugTools::CompareImageToFile{_manager, data.maxThreshold, data.meanThreshold})); +} + +void MeshVisualizerGLTest::renderMulti3D() { + auto&& data = RenderMultiData3D[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + #ifndef MAGNUM_TARGET_WEBGL + if(((data.flags & MeshVisualizerGL3D::Flag::Wireframe) && !(data.flags & MeshVisualizerGL3D::Flag::NoGeometryShader)) || (data.flags & (MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentDirection|MeshVisualizerGL3D::Flag::BitangentFromTangentDirection|MeshVisualizerGL3D::Flag::NormalDirection))) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() << "is not supported."); + #endif + } + #endif + + Trade::MeshData sphereData = MeshTools::interleave(Primitives::icosphereSolid(0), { + /* The icosphere doesn't have tangents and we don't use them, but + concatenate() will ignore the tangents of others if the first mesh + doesn't have them, so add a bogus data at least */ + Trade::MeshAttributeData{Trade::MeshAttribute::Tangent, VertexFormat::Vector4, nullptr} + }); + /* Plane is a strip, make it indexed first */ + Trade::MeshData planeData = MeshTools::generateIndices(Primitives::planeSolid(Primitives::PlaneFlag::Tangents)); + Trade::MeshData coneData = Primitives::coneSolid(1, 8, 1.0f, Primitives::ConeFlag::Tangents); + /* For a GS-less wireframe we have to deindex the meshes */ + if(data.flags & MeshVisualizerGL3D::Flag::NoGeometryShader) + for(Trade::MeshData* i: {&sphereData, &planeData, &coneData}) + *i = MeshTools::duplicate(*i); + GL::Mesh mesh = MeshTools::compile(MeshTools::concatenate({sphereData, planeData, coneData})); + GL::MeshView sphere{mesh}; + sphere.setCount(data.flags & MeshVisualizerGL3D::Flag::NoGeometryShader ? + sphereData.vertexCount() : sphereData.indexCount()); + GL::MeshView plane{mesh}; + plane.setCount(data.flags & MeshVisualizerGL3D::Flag::NoGeometryShader ? + planeData.vertexCount() : planeData.indexCount()); + if(data.flags & MeshVisualizerGL3D::Flag::NoGeometryShader) + plane.setBaseVertex(sphereData.vertexCount()); + else + plane.setIndexRange(sphereData.indexCount()); + GL::MeshView cone{mesh}; + cone.setCount(data.flags & MeshVisualizerGL3D::Flag::NoGeometryShader ? + coneData.vertexCount() : coneData.indexCount()); + if(data.flags & MeshVisualizerGL3D::Flag::NoGeometryShader) + cone.setBaseVertex(sphereData.vertexCount() + planeData.vertexCount()); + else cone.setIndexRange(sphereData.indexCount() + planeData.indexCount()); + + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{}.setProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) + ) + }}; + + /* Some drivers have uniform offset alignment as high as 256, which means + the subsequent sets of uniforms have to be aligned to a multiply of it. + The data.uniformIncrement is set high enough to ensure that, in the + non-offset-bind case this value is 1. */ + + Containers::Array materialData{data.uniformIncrement + 1}; + materialData[0*data.uniformIncrement] = MeshVisualizerMaterialUniform{} + .setColor(0xffffcc_rgbf) + .setWireframeColor(0xcc0000_rgbf) + .setLineLength(0.0f) /* no TBN */ + .setColorMapTransformation(0.5f/sphereData.vertexCount(), 1.0f/sphereData.vertexCount()); + materialData[1*data.uniformIncrement] = MeshVisualizerMaterialUniform{} + .setColor(0xccffff_rgbf) + .setWireframeColor(0x0000cc_rgbf) + .setLineLength(0.25f) + .setWireframeWidth(2.5f) + .setColorMapTransformation(0.5f/coneData.vertexCount(), 1.0f/coneData.vertexCount()); + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, materialData}; + + Containers::Array transformationData{2*data.uniformIncrement + 1}; + transformationData[0*data.uniformIncrement] = TransformationUniform3D{} + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({-1.25f, -1.25f, 0.0f}) + ); + transformationData[1*data.uniformIncrement] = TransformationUniform3D{} + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({ 1.25f, -1.25f, 0.0f}) + ); + transformationData[2*data.uniformIncrement] = TransformationUniform3D{} + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({ 0.0f, 1.0f, 1.0f}) + ); + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, transformationData}; + + Containers::Array drawData{2*data.uniformIncrement + 1}; + /* Material offsets are zero if we have single draw, as those are done with + UBO offset bindings instead. Also no need to supply a normal matrix. */ + drawData[0*data.uniformIncrement] = MeshVisualizerDrawUniform3D{} + .setMaterialId(data.drawCount == 1 ? 0 : 0); + drawData[1*data.uniformIncrement] = MeshVisualizerDrawUniform3D{} + .setMaterialId(data.drawCount == 1 ? 0 : 1); + drawData[2*data.uniformIncrement] = MeshVisualizerDrawUniform3D{} + .setMaterialId(data.drawCount == 1 ? 0 : 1); + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; + + MeshVisualizerGL3D shader{MeshVisualizerGL3D::Flag::UniformBuffers|data.flags, data.materialCount, data.drawCount}; + shader.setViewportSize(Vector2{RenderSize}) + .bindProjectionBuffer(projectionUniform); + if(data.flags & MeshVisualizerGL3D::Flag::VertexId) + shader.bindColorMapTexture(_colorMapTexture); + + /* Just one draw, rebinding UBOs each time */ + if(data.drawCount == 1) { + shader.bindMaterialBuffer(materialUniform, + 0*data.uniformIncrement*sizeof(MeshVisualizerMaterialUniform), + sizeof(MeshVisualizerMaterialUniform)); + shader.bindTransformationBuffer(transformationUniform, + 0*data.uniformIncrement*sizeof(TransformationUniform3D), + sizeof(TransformationUniform3D)); + shader.bindDrawBuffer(drawUniform, + 0*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform3D), + sizeof(MeshVisualizerDrawUniform3D)); + shader.draw(sphere); + + shader.bindMaterialBuffer(materialUniform, + 1*data.uniformIncrement*sizeof(MeshVisualizerMaterialUniform), + sizeof(MeshVisualizerMaterialUniform)); + shader.bindTransformationBuffer(transformationUniform, + 1*data.uniformIncrement*sizeof(TransformationUniform3D), + sizeof(TransformationUniform3D)); + shader.bindDrawBuffer(drawUniform, + 1*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform3D), + sizeof(MeshVisualizerDrawUniform3D)); + shader.draw(plane); + + shader.bindMaterialBuffer(materialUniform, + 1*data.uniformIncrement*sizeof(MeshVisualizerMaterialUniform), + sizeof(MeshVisualizerMaterialUniform)); + shader.bindTransformationBuffer(transformationUniform, + 2*data.uniformIncrement*sizeof(TransformationUniform3D), + sizeof(TransformationUniform3D)); + shader.bindDrawBuffer(drawUniform, + 2*data.uniformIncrement*sizeof(MeshVisualizerDrawUniform3D), + sizeof(MeshVisualizerDrawUniform3D)); + shader.draw(cone); + + /* Otherwise using the draw offset */ + } else { + shader.bindMaterialBuffer(materialUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform); + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + }; + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + /* + Wireframe case: + + - Sphere should be lower left, pink with red wireframe (and no TBN) + - Plane lower right, cyan with thick blue wireframe and TBN + - Cone up center, cyan with thick blue wireframe and TBN + + Vertex ID case: + + - Sphere and cone should have both almost the full color map + range, one tinted pink, one cyan + - Plane tinted cyan, with just two colors + */ + 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, "MeshVisualizerTestFiles", data.expected}), + (DebugTools::CompareImageToFile{_manager, data.maxThreshold, data.meanThreshold})); +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::MeshVisualizerGLTest) diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerTest.cpp new file mode 100644 index 000000000..e9bad7799 --- /dev/null +++ b/src/Magnum/Shaders/Test/MeshVisualizerTest.cpp @@ -0,0 +1,302 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include + +#include "Magnum/Math/Matrix4.h" +#include "Magnum/Shaders/MeshVisualizer.h" + +namespace Magnum { namespace Shaders { namespace Test { namespace { + +struct MeshVisualizerTest: TestSuite::Tester { + explicit MeshVisualizerTest(); + + template void uniformSize(); + + void drawUniform2DConstructDefault(); + void drawUniform2DConstructNoInit(); + void drawUniform2DSetters(); + void drawUniform2DMaterialIdPacking(); + + void drawUniform3DConstructDefault(); + void drawUniform3DConstructNoInit(); + void drawUniform3DSetters(); + void drawUniform3DMaterialIdPacking(); + + void materialUniformConstructDefault(); + void materialUniformConstructNoInit(); + void materialUniformSetters(); +}; + +MeshVisualizerTest::MeshVisualizerTest() { + addTests({&MeshVisualizerTest::uniformSize, + &MeshVisualizerTest::uniformSize, + &MeshVisualizerTest::uniformSize, + + &MeshVisualizerTest::drawUniform2DConstructDefault, + &MeshVisualizerTest::drawUniform2DConstructNoInit, + &MeshVisualizerTest::drawUniform2DSetters, + &MeshVisualizerTest::drawUniform2DMaterialIdPacking, + + &MeshVisualizerTest::drawUniform3DConstructDefault, + &MeshVisualizerTest::drawUniform3DConstructNoInit, + &MeshVisualizerTest::drawUniform3DSetters, + &MeshVisualizerTest::drawUniform3DMaterialIdPacking, + + &MeshVisualizerTest::materialUniformConstructDefault, + &MeshVisualizerTest::materialUniformConstructNoInit, + &MeshVisualizerTest::materialUniformSetters}); +} + +using namespace Math::Literals; + +template struct UniformTraits; +template<> struct UniformTraits { + static const char* name() { return "MeshVisualizerDrawUniform2D"; } +}; +template<> struct UniformTraits { + static const char* name() { return "MeshVisualizerDrawUniform3D"; } +}; +template<> struct UniformTraits { + static const char* name() { return "MeshVisualizerMaterialUniform"; } +}; + +template void MeshVisualizerTest::uniformSize() { + setTestCaseTemplateName(UniformTraits::name()); + + CORRADE_FAIL_IF(sizeof(T) % sizeof(Vector4) != 0, sizeof(T) << "is not a multiple of vec4 for UBO alignment"); + + /* 48-byte structures are fine, we'll align them to 768 bytes and not + 256, but warn about that */ + CORRADE_FAIL_IF(768 % sizeof(T) != 0, sizeof(T) << "can't fit exactly into 768-byte UBO alignment"); + if(256 % sizeof(T) != 0) + CORRADE_WARN(sizeof(T) << "can't fit exactly into 256-byte UBO alignment, only 768"); +} + +void MeshVisualizerTest::drawUniform2DConstructDefault() { + MeshVisualizerDrawUniform2D a; + MeshVisualizerDrawUniform2D b{DefaultInit}; + CORRADE_COMPARE(a.materialId, 0); + CORRADE_COMPARE(b.materialId, 0); + + constexpr MeshVisualizerDrawUniform2D ca; + constexpr MeshVisualizerDrawUniform2D cb{DefaultInit}; + CORRADE_COMPARE(ca.materialId, 0); + CORRADE_COMPARE(cb.materialId, 0); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void MeshVisualizerTest::drawUniform2DConstructNoInit() { + /* Testing only some fields, should be enough */ + MeshVisualizerDrawUniform2D a; + a.materialId = 73; + + new(&a) MeshVisualizerDrawUniform2D{NoInit}; + CORRADE_COMPARE(a.materialId, 73); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void MeshVisualizerTest::drawUniform2DSetters() { + MeshVisualizerDrawUniform2D a; + a.setMaterialId(73); + CORRADE_COMPARE(a.materialId, 73); +} + +void MeshVisualizerTest::drawUniform2DMaterialIdPacking() { + MeshVisualizerDrawUniform2D a; + a.setMaterialId(13765); + /* materialId should be right at the beginning, in the low 16 bits on both + LE and BE */ + CORRADE_COMPARE(reinterpret_cast(&a)[0] & 0xffff, 13765); +} + +void MeshVisualizerTest::drawUniform3DConstructDefault() { + MeshVisualizerDrawUniform3D a; + MeshVisualizerDrawUniform3D b{DefaultInit}; + CORRADE_COMPARE(a.normalMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); + CORRADE_COMPARE(b.normalMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); + CORRADE_COMPARE(a.materialId, 0); + CORRADE_COMPARE(b.materialId, 0); + + constexpr MeshVisualizerDrawUniform3D ca; + constexpr MeshVisualizerDrawUniform3D cb{DefaultInit}; + CORRADE_COMPARE(ca.normalMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); + CORRADE_COMPARE(cb.normalMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); + CORRADE_COMPARE(ca.materialId, 0); + CORRADE_COMPARE(cb.materialId, 0); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void MeshVisualizerTest::drawUniform3DConstructNoInit() { + /* Testing only some fields, should be enough */ + MeshVisualizerDrawUniform3D a; + a.normalMatrix[2] = {1.5f, 0.3f, 3.1f, 0.5f}; + a.materialId = 5; + + new(&a) MeshVisualizerDrawUniform3D{NoInit}; + CORRADE_COMPARE(a.normalMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); + CORRADE_COMPARE(a.materialId, 5); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void MeshVisualizerTest::drawUniform3DSetters() { + MeshVisualizerDrawUniform3D a; + a.setNormalMatrix(Matrix4::rotationX(90.0_degf).normalMatrix()) + .setMaterialId(5); + CORRADE_COMPARE(a.normalMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f}, + Vector4{0.0f, -1.0f, 0.0f, 0.0f} + })); + CORRADE_COMPARE(a.materialId, 5); +} + +void MeshVisualizerTest::drawUniform3DMaterialIdPacking() { + MeshVisualizerDrawUniform3D a; + a.setMaterialId(13765); + /* The normalMatrix field is 3x4 floats, materialId should be right after + in the low 16 bits on both LE and BE */ + CORRADE_COMPARE(reinterpret_cast(&a)[12] & 0xffff, 13765); +} + +void MeshVisualizerTest::materialUniformConstructDefault() { + MeshVisualizerMaterialUniform a; + MeshVisualizerMaterialUniform b{DefaultInit}; + CORRADE_COMPARE(a.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(b.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(a.wireframeColor, 0x000000ff_rgbaf); + CORRADE_COMPARE(b.wireframeColor, 0x000000ff_rgbaf); + CORRADE_COMPARE(a.wireframeWidth, 1.0f); + CORRADE_COMPARE(b.wireframeWidth, 1.0f); + CORRADE_COMPARE(a.colorMapOffset, 1.0f/512.0f); + CORRADE_COMPARE(b.colorMapOffset, 1.0f/512.0f); + CORRADE_COMPARE(a.colorMapScale, 1.0f/256.0f); + CORRADE_COMPARE(b.colorMapScale, 1.0f/256.0f); + CORRADE_COMPARE(a.lineWidth, 1.0f); + CORRADE_COMPARE(b.lineWidth, 1.0f); + CORRADE_COMPARE(a.lineLength, 1.0f); + CORRADE_COMPARE(b.lineLength, 1.0f); + CORRADE_COMPARE(a.smoothness, 2.0f); + CORRADE_COMPARE(b.smoothness, 2.0f); + + constexpr MeshVisualizerMaterialUniform ca; + constexpr MeshVisualizerMaterialUniform cb{DefaultInit}; + CORRADE_COMPARE(ca.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(cb.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(ca.wireframeColor, 0x000000ff_rgbaf); + CORRADE_COMPARE(cb.wireframeColor, 0x000000ff_rgbaf); + CORRADE_COMPARE(ca.wireframeWidth, 1.0f); + CORRADE_COMPARE(cb.wireframeWidth, 1.0f); + CORRADE_COMPARE(ca.colorMapOffset, 1.0f/512.0f); + CORRADE_COMPARE(cb.colorMapOffset, 1.0f/512.0f); + CORRADE_COMPARE(ca.colorMapScale, 1.0f/256.0f); + CORRADE_COMPARE(cb.colorMapScale, 1.0f/256.0f); + CORRADE_COMPARE(ca.lineWidth, 1.0f); + CORRADE_COMPARE(cb.lineWidth, 1.0f); + CORRADE_COMPARE(ca.lineLength, 1.0f); + CORRADE_COMPARE(cb.lineLength, 1.0f); + CORRADE_COMPARE(ca.smoothness, 2.0f); + CORRADE_COMPARE(cb.smoothness, 2.0f); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void MeshVisualizerTest::materialUniformConstructNoInit() { + /* Testing only some fields, should be enough */ + MeshVisualizerMaterialUniform a; + a.color = 0x354565fc_rgbaf; + a.lineWidth = 0.765f; + + new(&a) MeshVisualizerMaterialUniform{NoInit}; + CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); + CORRADE_COMPARE(a.lineWidth, 0.765f); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void MeshVisualizerTest::materialUniformSetters() { + MeshVisualizerMaterialUniform a; + a.setColor(0x354565fc_rgbaf) + .setWireframeColor(0x9876fadc_rgbaf) + .setWireframeWidth(3.5f) + .setColorMapTransformation(35.5f, 0.5f) + .setLineWidth(3.0f) + .setLineLength(4.0f) + .setSmoothness(5.0f); + CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); + CORRADE_COMPARE(a.wireframeColor, 0x9876fadc_rgbaf); + CORRADE_COMPARE(a.wireframeWidth, 3.5f); + CORRADE_COMPARE(a.colorMapOffset, 35.5f); + CORRADE_COMPARE(a.colorMapScale, 0.5f); + CORRADE_COMPARE(a.lineWidth, 3.0f); + CORRADE_COMPARE(a.lineLength, 4.0f); + CORRADE_COMPARE(a.smoothness, 5.0f); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Shaders::Test::MeshVisualizerTest) diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-vertexid2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-vertexid2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..fc8195fd8ee3878d24891fe5dc00caa5b6f17279 GIT binary patch literal 6639 zcmcIpXH-(}pVGYsqBSRE#mh@bn2Aw3xLFy?o57fAcdBs)RPxus%XSKCCAtPu4e{|?*>#bk|A z2fLF&W2tr1D8G5+uRfYttJUKJmF~ORg`q9n$VS`fI#&Eg)0D~qnXedy&lnFL4ScR( zR_2=5+!@+*)3*JJz4`)=Jn2mRB~b4bb?gu~ZjyZTm%Lu4M{xk(V#1EsaFSHKG$k*y zotM|jD{kbKe&!W7VZ}a{9Cnm6->PCPZ~cDOC+t2*(OOvp+ARgO}UJ zD{A81ujf6gM?Q6<%&D`=)yMInr{!hPkox$3;!OC)eP~{g^C~ za-V_B2kA7%J(#_CB@Mg>b-c0<+!vLcidXD+&xj9?h`M`3Ljlp8)t6{X!v#y+^0&Nu z_0Wj8sj4jf6^ZwciP}=4zL03jCR)?k z?a3TvJWqAWNpp@*o^;fl#|rL37wE++Z04q@M#PX-VRU@-nL-;_i&xMdW*{5u@b1Qy z2{*GVpA!xF?9K$P>awFc%7r{7B9D%w_Ipr&%E*9;*MGXi1T-Cln|-4bpfbL&ut;A+Nw z{*GpEO9oGU$(1@ZiCptr=M=vVsekL}jm`Wm4cy?*c6(~9{`_pWrgiiJ)tvF<25fUs zUj)rLuV3-9+mXS*>t6Dz?nE&p(jH{-8zT*5^e))@(2>`e3nB~16LK_h?KyuSSWuK|F-#9(| z0>1tX-!QiC0^W@Hq(IC$YtDeu00naFsJA~-X?pRA>E#FJ@e0<>e5=$<+r)gU3r|ds zzBLW0wO-dE0i_(&(cl@_jx%IPh#Co{hxpkS?4P0xWP2UbE18=c?^JMESRCbA5GP1aA91Z@$f4seZL%SkCM1hr6U{e_1G;F?7io~Ix{91KmfVQw+|VKd$s=AduiUo)-ohA> zBFw9Jzx0Q~T{2u0XGY~DI$V5g7E))kP&soXb&zOFrO%_BHrrDq4)0!LtGg2ly$XU4 zv3$e$L8rn_+E7G&5xXN1nbDKlh8(l1%^jf%qC`J7yOnK|ch#ljgnPlCvXoWw8_Q&= ztL6Ay5G%Y|G6ar7fO}Ef5IMyACph~@Tq4balzpbrQDDT3hjiM_EJ4*(19{{B^k_?S zR9_fP?L}r>+A$6(=Ww+JqV`6?(CnK$n7(+g^v(u(>@U&-V?86LduMHuV=*ar*s-^! z>s!PVNqd~dmvq&G-I0W7_M~>qRxeuF z?z!Xhh<~FwImbHpnsZ5nTj5Sw>Kb{}T&Er zyHy7IebJ*l$v`gA>7h+hT}U;4b0ZI=Itx-2{*P35|B%Db;1x^{>}P-Sj-D@#`&Ab2 zD@$B1OIaz0me`Y}hY!6WUz?=F&u$sW8C^Btsbi38CXuUAHUu^~9eh9J^b6+oV#~~1 zoZ|DYii6$-J7lnL@(P+WFgk$8`_R~L0lU9E1K0!Pxm#rUJ7jbo+b2~V^44o0DN7R> z#tk|RwAF=#3d4!DNI4y~#J-P~IEZl+WTX;B7sZMQH^3yOB!>Hm? zvTP8p`B(Tw(PH2#x*)+oVT1q%QhJ;niIu`wVg6N@FR1f!4PlnMf zMY6Sly{BdpH7vijb_V;6J;*?GkMQXVCcV;KM*Xo=GsBY% zz&~-i#UA6SHCsAIqlJi$qF6;!Ggf!2=Tz$VogJd&cLNW#IxHrCa?@<#H%H)RjBdks zGb!JFZO(Md^9AF4z11H|?}^kZG*fUtTDPb7JaS=J3kTiE+Vr819}G-L`cGT#Kw%j8hFZ{_1h#sC9z&P(c-~%6Z*> z6Xie^&f71y7vSJ@vy%^`r}* zEwQcb+KA3z@zdYL%AI0WG^e7#Ed325yvc5bda{(tiz*Ua^K<*^j1x>?+8cknG}y_pV73rq(b`3)7meTOPpm+t2YiZ-9A49KID?uH z6Rs0AD5PD8E-{QuMmT?1BZ=|ik7C#v4c=q){l*1D*;i%!w|-*Hei5wn^xoj4Dw+d} z$T?aLoD{uEbj`_wq!W5+2U{J}D;OW{vZ$l{%HPG2W1Nk9_!yZUFfu<(%k00y$YhgI zZ-3)&myPOgH`9VxVaMI>=y1f<8Q+^cQIJ1N*hSjFBhBormyEjGR^(wJ>=Pz-HtxCH zhyfu1M!nbKGuK2g-JDo%%h~GS5GHYk8IvXma%Tx2E)ZcqMCsLJL^KXBc+O}{wt+5X zi^L($j>f%K8Ta&K_L?}w%zmj2Cy3{8SmYGt;gUFBkTX;G_k2;guef@Z1VZ#??{~JI z_HRqVa8-!7biOE%>oC#O-p87=iOV}EbUNqma$}qTWVk`=Vwe6D&~rwE*8K2U0kOjry`sIqa%($km80ub@a$7 vXCP`iF0)&1*uv4crB6HEi=N_^Rdv=7Eb7yg@qO(tDp;XYz}IqK*R}rwx6`(6 literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-vertexid3D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-vertexid3D.tga new file mode 100644 index 0000000000000000000000000000000000000000..e225aca568339d3086bf4bf33bba56e091baab62 GIT binary patch literal 5818 zcmb`LcT^ME9>)!9K~$6;LKUP<8YP`ongn}U%i7lhSP&~>M-&BA5J4%TD4>Xlf?%Nt z0@4$^?uu&zv4YY|LXw&Hn=rDx^WHn>{SnT&=Z4AseeY+!^Si%0xkH9%4EgytXULo( zw6y-;b`{1_#x1<3wYXGgS*h+HWqPac8~Buy*H;*BtfXwNGTvTo5?F1zyT*Kf zjYV*+Wk{`6n8^B=$R<)`7cE9ki>dKqTB4YdB(_f$J6seyri(%GD71d%1;tv6O0<`j z>MSqQU3FjIyWC)HIe9|`WmBcm)+&>LYSUfSW_xSK2h~~}s-18|WEC#5i4fUFiIEsF zHC9YJBc`7fGtZ0dQ^gKxKb22{x+YzAEv~_^e82Fxm)pLHE-^FQ3%7VZIPG4NPfe*K z2e%>%<*OYP7m)7{VDvF)l(F-QRd}PIh~H%tw6RoaZp=qY)gfW#Dq;R6uWTRT>6spf z-Q9A0g`#7gm06A%Pbo*6%vbfz<6|)p(2w(5@i^7FL*Sf%UJL2TKk>z+HcVKy$Ezk( z_~4W~P!tDvU0LCIOz4)r+_Unaur`gCQ)3qWk>b}o*$q2Pgi|!<6sgcALNyY4ZKUfS zSRjh<6vudoFZ^0{ja8aQ18W|fajia#yO#X>>DzuDHjeuVFKpVB6=-NZ$l$YfAdUDhk6M&8QW_IokIZJE`i7vV*5J!4~ zWtl65S>D3haADPTr>jqmAU+%#W%P_fP1PAk&>e?fZ%>EAwMWKaM0^7oaxJ9AW@jf0 zQfWosf_0K9L2dMtpYd;RR-2dKWQvm}LNXR^^}4vkvm!`Xdr>d|IK5DX4ibR)>M{uE z;z4ydW__C=429^t1vMT1jBy)q}jL$K_xv0%|tf?8Xt>xR3y`7 znMiLQtuKp>CJ52J%VbNobvuVOTPD;i)3ey7CY=lUShm;ewx4i-bZY)+)p!tAsAgfEqUq4S-^|vy*-NXSPBv zi&C6L$31_Nj`GS^p{)cd{^-fm(d|t6WvIzea*!4g^5q4s{vG}07slfjMpYN%VJq!z z1Jd*a*kPD67BL(3_mR%n*UpSm=Ip&fJJ?1b4L~at8fBC)Z?x)c9Zc#gna)NNk>*E0 z#CXxf5Vte#_s|PuNS4Cp5{g_`FiIqhCqHQK-@!J9^yWJ#Qe3dG`3m1%9Zrx2dF7U{ zw4g-&B-QZDQ=zQ{DZbtfrlNt6jPzHZsJG;H8K_kfIz9$9IU*@g2461$27487$@ z3f12x(e~-mT+%jbcC+TBTk6IxysL~uTbd7ZO(WdcNo>2-*tnU`v z4q$rRR-vH;CSKe1ywKf9PXXhvuXdRVg8-@eiAq^#y7n!y_N|m<8B`;Dx|gAL6Hh|3yLi4QE?v(2k6{xX{gS@L3BEv z*gi0Y@x0kK9o5~`JKW_xpplI4{9;$D?%ptNb*Jeu1)h(OT99wgpn!T{2ViRo{cZ&! z%6(DY^`;8FBhc_yFKzIG`mv5tBsC3_kDAjw!nJ<*#J9r*ui?p#ng7FZZy&5{{IS9! z=_zo=qE`0J^Y3vR;Bv_wsXEsNacO}CNM|Y366+MyAW*8mRif+PqqDvnmdCi29q`#A zPWeFVJ|XQDPH55sg*OpSQJ<8fLca)A{Iz);Q=SI<20dQ}4-1MN)D_z_6acNxp_XS* zi&&JJt$?BpwVENzaK{b~5dE5?t~#d%5sh`jLXh@yTK!wvyL$TTMta?6#-HDysNq}3 zgEmHO2fl$+b^|q4avK7bFY+UtZ>Z2C0!R592bQ6|{36s+co%@(mMXakn*h>X1p6#t z!qP*2h>_;UNN3pqiy&KFb?y)>SD~kj;AgAwM*mHjmo-9l*o5-n-@oy2&HQtj$;(Ij zwRJjWZ*@xE>l8KU7B=eUebl@4SwHuSLH0MpYfY4^%|_|pO)s^WrLuG}GtL^eSKL z{5O5$%im2ewwNckS|qhiNNl%`@31B5hmmwaKZK-xkTfocGi#f=+VHhA4DAAG_$>I{C}(;6La*$d&h1Zn{qPyh#+QE#7PpD(ushXBJK4pE=yo7! z1(U|^SCa1{joLxd*rqmevzq!wwGr#phWY+7bhSX+X08#kht5cKbs}at1HExkgAVwU z_QN!_b^L`k%X95kXF6$heUk^QP#flLq%p-+*Ji4L9WXC3qpq=}`B~G0nanhIR`o*sS0k>U2#u2d z-Q;Y$bwrOtcrUA;Ge|mdZ=dU~K92yIaI0+UCix6M`RreYuAymi-1TgylkMgiBg@RG z-j+0f8~P3^1DKCE+F$;uj8iL>f%#M?6$tl9U3TMyp4((oHp{1Pl+RkPnCq*UzXtt% zCAws$q1^%#WVt!jcLHsbEj^IR2x2lr9ql7I4zU8qbCVnaxY3UXs<`r5Xk?%T*)8&E z{_>d{6m!-p=B+^&u0$6vN0%){|5${sUV!`!nV;slOKlbkNO zu`-3Y2_KZ2IH^+eu!IM)e(RO*!e#;IJu5ijP}ujh3K;c7UYQVkqijxL{$ZJvs40|ifXCn&g~dtK4} zE{X#a6$h_-DMed_u7ZowV#KbW)o^j(Cf~}d_B-TZs*-=oaQeq4!ykE!rxRLjD3m=Wab#Nl34)AzkJDiEwaGvZKw=aStjpRt8 zIFji94F4X$Z{5j9PjdP)XoSvr=(SzZAZ}kIM|y%IImzygVfUP7b;q*0<5^t^Ff}9q zOx?U4kL}pY?>@kjhVW!*bb>;uAdny7%fkCZSe=Qij&rQGBv#u6R%`O$K*=GVER-id z#+9csn~Tf|g$vM+gQ@$+vwozqzF%TBr?HyS+25|fgMg?$WfXdX3l3m$tk!%Bh{0!8 z#Fb*xMkH+V9``;Uh@hckI$eS&#%iDRF?~?Dg+f3g7Qki{mRySOYrSWscB<{ z(YqSMtZ#k;@dRnhZ_1-!nrJzU#PwCKOUjH4u`(l%SxeU!n!7|Uxr?7%6(}v~DZPPq z$$_>)me*)Z6GDrOM;~9f-wBhOv2;?LNNn)u$~!h4tPh-2$gUCtMFT)!P8DZoZyXIk zbPQ|v7XGHv@-p8&q$m}b5KA1ub&w~VQTC7XL??DkJ$*rO{Han$~(3=W}CxRq~HM*>p zk@1$2kuni|VW*K!G)R$4zVf7>f3}=Rq*EgCDn#&5*;AsZi$px_MC>e7DUb@6)@+%v zuuM)N$|;3yatcQ1ai}|}6|hCmN~C}iM^6xrVbud+zy~o5!u?5@+x*KTg9klvimw<5 zqf_uW6>wBMR4Lesa)5IT9DsU6U4ct--1*9maUpl)xY15=W97J1a5ZU%>5ZpF4}jrVEV>xRJchl8~;z_$w<32~j0k zy5T-p$Z;tK>bMj)39gWmXnbf^Feg-cJ1)yV4h(QVCAyFpXG+^qr~xfW$T+9al&VT- zG+Z=EKvV5^W6<^rgp^?5EMa8LLwFG1@2hh$C>{j8IrXd4JC)!QXAbQd9TeD9U_2D~ zsX|IJ3OH6_A=HE`joqPhOJPk?AF24Lv!H^dVBw1ifmK_e^ya`e?KISx2nBN2#il;} zv*k>v9;-8<8m?gCjhJJWaL^F?J;D!HU{k?FQq5i1Lx77QbF;f=(V8mIkhD+gQ0g?O zt4U~(6J0UPVBnC>0gM14%DShdj_d6dj^#cb9+)r3rzWkAPmNuH?}ACnKJC!$g;q#d zGH}XSv4V?3)VhRP9j_Q1Xf|ZzCS7}@IiQ3&7Lybv84M9<-)NtJ!auUy zp3oFjJsuIV7iojYF&EMtD_)DnV`cChC^U$BfgEN7^ky_k^eel4d_a{#p}-SmZzI8B zmnM3>^a2i}!fyn0TQokjBgPe#76D=vXS>77Zdl=^R&Yc=5QHBja5Q=j8Uynl{UF(k zfs$jRxhSdjb4G111@fGk=S><7KAps(9UXSeye_LBKKq^!H}%-e@_Qz4WI)Nx$r*00 z*ghs6x#H;Vk}w!4UpnVO{F=fEqfML^het#K0|c7>O3EEEIJE14D2QSpbR?RwLNon;@soz=%1QinuIPbT>^>qz0NK^g9b z+Jv?2_f%Ki-}R{*i>FO8yXqnPvnWsWU>P#CJJx4-xD`%(sWsy;d`u6OZ!bAr6a7X0 z*cZ{&p^z#n5oJ=cnmVe^|2O+}6UW{A>Z6|a>oz^BLD@M7cI`UE!oa_!pWsVlr=7TCY`pxe(zgoi`OOuz91q@yg?)#*(lQqySWH z-jD^L@mxaA^8*l}hTo};3jyfqyz%6pskl2-~tt zOyRb|lSOa5Y$EzTWjIKeQG7Rvoz_KWI2(eHfxP))k+>ht0AN-m2Tb zp(u0$XyDNHWa+tQ4|+Ct$e^-CGctX>aAbS#R37lYa{>lo(^ejYDk8ifsv>H&M!NSu DOo`Iz literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-wireframe-nogeo3D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-wireframe-nogeo3D.tga new file mode 100644 index 0000000000000000000000000000000000000000..00291cd9fdd1063349c2f454f4ecfeffa6d5f6a8 GIT binary patch literal 4183 zcma);2~?EV6~|}!W>}R~agB%z5H*!RqIL<9#8^{fNNi%$8WY8&X?jeW)-9(=47lP7 zNi}KXZlV=ErzeeiV$!;usu5;jh8c!MSp^XU9EAe14E_JV_vtso4Cj>dy)%68-Ftuc zzIX3^pGFg?x&N1{N!1L~6oiF^{l6mzvO2S*w?7&X1Kbq z^!1rO!O%hktC<*C7=vRI98bSg9vo5-G^oJfXAX_9%zvwT-jXUj&n~RMGiyOPp75a- zNYl0vb}L_fwx8*2Pt!Xf7&0c-4$}ca`4LgphbGukmsSlQQ>fFK5|c|e9BDq&+--tV zU*DB(SIYcy#kwD?w-@Dj1F}2;+aVtY)6U90H@*X4=Sb%`RtWVx{AH3)|yH;zOHmfZAlrzuO=gtH2 zoU>=m-li$jiy<>=obAnzZyd3Q1mmkP&0|Spc zH3RQ{`+sP|Ar1;Hz{W%H^D}otBbgSPFmw-~=}*`!Qqy`;QW`~u0CGe z0Y*B8-W|La6T0skyZ@BcFyWCRJ&piBKbdpY3!5<@c8e#LNi0c5GqQ5&2X&|GyFg%g z9@ur5Fy{95Ax31se_%eM)=NyEjTmaMYX_um_M|fFBzTYWTAtm269j; z@9p*VqsQBDx?a3>1M)I`^I+%AHFd{JxVc`u^_VoE{`OpKUAX)@QW_3=A=eLx+~kSm z#u|_jNF$Ub%H;eVM(OiN{XyM#xoy$P>*x}o4jpMlbMOnuhm8#w>JD!h5WmqIA0$cU zgeOY$dNT_0m%pn%aHTD;w|_3vp+LcZZDppjUz-KK<<89-{qdwXi0GG_0_e}@pg^YG-QM|HLYsLH_i*Kz3|%T zXPz;;%*zLEw{IVKME!jLdcm@qO*t(;oqHAa8W7Bq%{t)}jSKX}@7IovXPb!_W}UmJ za`W+)vn@O?H0HQR9nrCNxZ&jkv&^>-%uF{Q*i%AqqF#6$su ziK`6P8a77UKyp#MlUWbv(1^U4Y#zRuY&iXyfNcDG3pNqYzixHUSy%zHsE3dU3tT`m zxgy_1a=W>F&o%RfYv?OrdUnvQ12WlL-I%-ugOMuVHFP7*SM6*J8e9Th|ouVT!NCoxjpgR)ni3$ zV|c!<=v?th1D4p6hgxte?Y_|Z;%}?L_9F2<_wy!sVkGz&Im|jDx)3FT8+R>+5w~jE z2X*M6I7Hk!@kzYV+2zzQ_*v3FJygs5!qTD{jf}BnV(4%@Mt|67?XPA>rTCF1?&Jqz)JZ*C6lS!J=ClfK`UoC#+ z*O|(+`irH+(wRx?Fod-FYoyl9q~hsQEJMPIXG{SAoLzK|IG(=c1C=e;qb>mxTviFllC#A0X z)1^;6Rxmh7L`agX1DYc#K0hd+`4C$rbyOPPq~@>I>(qXSa3a5S)adQF9Mzo+S|eXX7#8PzDcgmOI2^Y04YJC-hNVSV@KFx zMz;O1UsYp?DsTF>+=E!QjUTOWWF1=3XzD&(s_G=7vhVBEee?#F1&6*JzzDWRMHD|i z`CiRQNZ}EUK>XiTdkPK4Iy@3`v@gp;-Rx($wdce8@(Ka3P5aiE{fxpsB@t(WqS@C`b#i}d`MN4F;^8}l4X|iJ0c`EzCNt1sF!{4N3ZKH%0Z(Vt;Ww~e3 zk|Lu;nYLSx2vhC0qxRS_s?3*=Gn#Z-l%+6obNhRERkXRPgK$amqfpngT_q<9Wg7nq qLqhh5Him!jP8E3`Vlq;Z=lo#|9rh75q)2fl9f!eR_x3)}|NjF7^3CA@ literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-wireframe-tbn3D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-wireframe-tbn3D.tga new file mode 100644 index 0000000000000000000000000000000000000000..c5c208c058ad2725bc77ff2eb49fc43f52b94542 GIT binary patch literal 5096 zcmaKv32>C<8OJxfn}a0AMmwX<)Umdx#E#*RG6<+e@EDK^5z#tTiXf*tw6-FHNH=#N zf-w*T6-WWoT3ghrsL+D8O(6ynkc1@%Im|&WVqy-+wf#Nc`|Y>i?zc;3-+7Pc|NQ^Y zdp++Ln=RVr{hMo>YrEc75+5HQs=S9S%ww}r?esO%XqqfLe3o>7%bPw+hRI^HdD193 zx>fHcdl5E|?PScTJW3c%MzeH_lWwuiR&sT0lWSS$-#6t_Opw=s#r-~~<>kEAdC%59 z@WlBk4^<>Bt$lS@`}()r*6nOtyS;VIw$_(&Tfod)S_^t3B?8j3cO@FULtS5bJjJh5 z?9lrH*a9|s@hgq_$9tyFt6K0}JsOZMT+@&^#htvj<3GK_#U4*GB`oW&7j^PT{F~EO zTKTtF_&4Jc`_Pl;-#FMc;ofu0Tuq1D29?Br`-aor?HG5r``0gi`!{z#3NDKM(}sbc z-**l#S)M2*)O9G&sZKY`C3rPs28`ijG0iL@t2E5L&9j5lqp3X{cdR8jKj5ZWQiDp|wv+;F2v#xQ{%!<6j-O!1O!z`P^K*X-Xo>@=TOi!u;#7fNh zO$~mT6uY76f;%GUR5w{vH;F0-@BYWD_nqiPKn{hUYAOTaU$hLa&c5*DgfqY0d?9sz zhgXt=$cce8Ii%!9U6LDumu$ZrWXI$OD;`@__hI!V*kxXRl4&AYH0;^-Mb|IpR?U98 zX8hf5oa9>C4at;{l35l>k#*dB`SY~js z(RY+R`)cEX3YnipES*6o^V@f_cggxjpccKfka`&wJy}oZ+$FV>W}M^debqe#d(d5s zL~?&gFLB+HSoXvV^^dKrn>M==Z^?wdg-s8sEfz^M{(Bk2&35ItlL)wk`9(cnkI0?m zXFj_U(5SD6J@1zGvBbBIEqnaA`W;8Rxj2b;on4XIWQjuN8T=5-+W1EyhxoeZvzp&O z+rMaS!)@csNt&NG4+MljmjDV*^>JZu8&|$yRo$CMx(oXy6XqW5nmGL&nb#7AY;W)| zwR(wzRO`i`O-(5Lj&T+Wh5p+Yfh>t-)xA z<;$)io{B&2X~*6FqR9;oE-ts;UtE%Pj7nd!+#`|`la|%7xCs->A6-_Lcc@EgYIkD) z$=;v*tnA)jR#`M9Q3cnOr%mBB^QzF~L! zgTFeDo|ZC=WReYQbkLNE!SBS&(-*_502x2B3D4m3nau|(FLA68WfN6a^Ik#H9OuBI zD6h__po$i`_)y?f&_)85^fI9$MBvosPnLuH_f3wO#O^ege6ayc#NrJ z3p8TY4X|v(x(|OjiY5mcc0ujHiHaXlY^2D?Ema0Z>@L0b;cyJ#%GJ3dsTEhrRng!4 z^hdDPEq|1q$)wm7FRgTndC3!9fTMvAnlywifI5Y~Nm&v?xG`ZBDMJ#U5I8GmO{H>A8+W

H9Rasfq!dMP% z3^O22P4q=u^JnCQ!%-3!+fg8QE2KYWKtPqg`0w)R*E~IY`1^n3FAr>>zBYy#kfATy z;_qU_e)^^xx{vO>k~d~RV3oRf@232K#bl&m?IO=`5&rxF<}QQ7MF4=Ql^0d<73H|{ zI}`fPz8}gH6991h@DR^El(>`8Q70n8Teq$W<%kIYFwmkUUZNLES{iIUWDS!+Ooo#9 zwXpj$nE zh62z~R`K>)PhEelveNKb_k(cKRVFnq9-+pyRU<`7#&>bLwVJ++i&`>mSyic}FDEaP zwq#Q(Y08jHEtR2gE;vK{q!?=`Y{_)()XE^Qi?SPVY_c;_yg6GSVvmwqb5uS+RG)^N z740TrlIWcZ0$erpWN%9Uc6^o18@i?kKEm)-?y4)%YwJW;a)%Im*;SiCZA^C1JO_t? crXiD{;?L^jy(GS;(7@<1ncZmX(-FV_12ggWp#T5? literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-wireframe2D.tga b/src/Magnum/Shaders/Test/MeshVisualizerTestFiles/multidraw-wireframe2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..cbc3a6d912c4c06281322330eeba171fceea8900 GIT binary patch literal 4494 zcmaJ_X>b%(5Z;-cJ#uPLKqwDTk*g5ol2Q;$4n?p4DG?FK4~=38Qi?#SD29+bAqQB& z075_rh*7x<6etP`%(+QOLP*GA639WaBqSl%X5-iMW_NaWW<$-Z%zM+_-}n0U>+VIT z^VT{3Ez&L0J)yG%1O#Y}M63%_X59+hRI@td?t~e|nD4;gvvj(=A&(c1dp;Q>6f5x< z<^_QjVW~}LYPuo6Hlp@KMc0ohjc+b1$F~FwA%EBqrdXf2q4V}%Nf;dT@o5`IEQbHM zX*WOq{?48&tykK5v-a%BRVkM}sBA~>>W!Q>4Vu%8fW=1Ds+{xy%HQiHZmq48y;w|CA z;~t>nHKFx5Dj3EV-O<(OW^zS`5}tL@brJy|nSf^waViesP&XhR5iZQ429scXkt2v^ zvghFk%teVqIqlDmDS$oErA|7*z%+Ss%63gcJZ=X&bTZ5_N`5Tl*gBS-A&qx8$B3@G z-erG(O_hqy*8b?kYSQ6wQvpH&AaU_=<>Dmb#)3kWi$B$@q7c#_g%}luFc{#Gby%_x ztul{zIax3YZusK0ywKP)bKz}Rpo8X}hqA3d3elPrIAlHSC`|T(iQzMFb`e=PgpP0jL53?{X&A1Q0s#b9(8y;^Bp(_)==qZ#gR3ph# zW@j`oPqDLNw}(8*AHDH##P~uLF!{qHFnLl5OrGXY0AZ0LLy;gEuzOw;189AGJrtEb zrStcQOp&8T=23T1E&)u}Lf$fq35L%iMqNtI)pfrzkIJ8t1qHuH7IbZtEJ&F{xduGt z2XCj>83imc7I@JhBBqq+KkmbbGHJ&2$Aa>Qngx%J0fikvo-(w-NkyBDn8mMk^ezsn zR1u=g=+P~dQ`Lmbh;pjNi zEPEgJ0}$*@g+m>nS&gFr5nEgITj+SuXrQB}VV zr$yxrevICO7TF$LhRYx39c;S)ScL2WN#38y_qJa7Z}k$q=MD<6{zg?5kG<=9 z%lV%wR=#ZsATem{m$W3#c3XPu)x8CyhvRGMf*D=aXP`xyMX*^fHXaBrne%eC(IDxN z?_@o@E#q$Rr`du&$IDco@HKQOT??J%$7>_k7LFgm@b*f&UYNp1z=Kdnaa!?f6R~o0 z>ez>QsgkMzA=Q>~plaO*`A-gJ+Pk&1A$~7;v&GLRi|1-K1-6_`48stbPsNulon!I$ zVX8k%sxo2d;pwUWyX?K$kg+`C8;!wX2N2!mLEL7&RIzG4#IsGt7z~G5#eQJ6>0n6i z0P=Cw7t5LshlXS5D}xs24jQ1OqtNTBI8icp8UzYo9MP7Z0y9*1g^r@%?`#P|kT6vZ zC)hLVqSoPHvW-G*#M-{zOsG+961wZoH|+Yhc=`ku-VX1}PH4|OSTtcI442HF+F5oS zI&|8G-uA2akH?lRnU&*9kqT%0B00nnULDt}zg(Wh;XfDr8dFGzp5BbMfA?3dS(-O= zAk%V$mlVX!rxWs@qO&YtF|ViTB2i?{aH>yxlrG--78`W5-l q(EnE8tK-(vm8a-68;d56Vpq^{P#GCV#ACeY8(|y?2aWOVu=$qHo!V2CYHgHMT#hb1_iMJ zK_oFI#v}$66H5{TNK;{1niN?&;vz+0m(98VE!+Ms6K3E1|L;BLp8MW^_r8xnU?=GL z^ALCl1`4vBoSgpexCX36hCq;IZySYi9FJs@z$DD>>rf@}Y5M6z1cn8?aD-r-!f^cZ zbnX7gx@B9+=B<7{WkI2*ciG{~4Tmn)`<<^p5LLJDY~9|7T40uWmjO*-1qf-GKiLHr z7Felxs8(m3!Vn_9E_^t^cAE~$uqsGCt*e{KAvS)AIc;8B z%O}k(4E?z&wX>TUw+B^g^lX_Ppe7|xnv}HJ6J9%NoHY3Rm+4B1s4q!VvVQPN=@(r; z%1w&t|5nifQ*3IBM$t~IXihYgCmIT_XrrHe;+(?Bd(8+(Dk6Q^qcB)qPH9=VwsfXv zF+f;}C7+docML9myLN}xyz89% zd00(ALL*Po->l?uo+RyL(WLo>E4G*4EO~?IG%o|mK$1*Xsn6b3eB@EQXkDq>G#N<2 z7`j;=H7gmylcZfI8ag`H`-nWbt_#s=QU;R2WcanL0|@8wQl40VS+QfL#fFW^+j3Zb zqmbsu6AOY-jQL&3YTbND4k%AA(5zlCPmk79?6J9Q;_L#H-lJwEqMLk+){sodZbe?h zUGi4%s#vkDeCnbiu!Ask3marsn>>b%hP3Tb*^;;hGy)W1TzbpnZmLQA^!F|$Wcyg9 zzM9j9B_83Lw`xa4^n+#;j?3iytpQcMENGslH!-b^VHUKB2kZ=~zMkK{;Xvg`Hz~U2 zo$5}b6!3@y5}&l8U`LLXuH0UJ?tXJhJ9WZ{B*lc81!%pzH0br3PJ^}tKMg0SqmcgX z=stbx)hy5A*rGUjoU-R-UkGj{4ZVe2#Ssp5sI4Zu5Z2;Y{ z(5QkxhROG8_O;{dd)gGNeYj|G|d0xIrI!$W+hkEJXU?9=nb|NMDY??SL-Q3v^nepo+6z(wk8#Ma!%8i6eY=N zJLB}j{VndaNmF)5-)~;LzI4*O!oWmL-2($J4teNN)6-Qo9jrEdJp(cNY!L51TNj_v zI(mXM>}C_f(KKxSe5u63p~CSlV^UgBMiIBEVt6X_sbFoS!k2J$m!bOB@2yOE(~Tw( zfMJQ+-F%RG;jZH~SVKgn-lEH&K5~qAgzkffzNakQrpuOYDht67;-#?GW*Fo&f>`RO zr;BinBCsR$48DRy?9;^19}PbR)c-P!((r(^$grSKymJ^Lb_+b*kr?MN#Myo9p1uDL zjOD(|u`0(fBmjrtDuxLTxB&u|!V-r*`NTzwr{a=3Vn|C`9A-dmSj<)xY}{yULbw3} zRLB^bIMKLTB5Y1S#|)uGRlrs?_lZP()Hn>;Ea3(SP!L!)lnF&)*(`L2gly{Aj?!NM zHy1_-H$YrA90Poca~^|MBGbw;%g&}D=14Cw#Bc)yD9eL#AU>CeblfNeFIhQ9*>oQ< zs2>|ohwIUB14MZ+2KW@`0tV#)$CE*CEAE`>C59MofB-#dbmEfemU^ zI{5e*RTmjm=>TYzNtL*y>?j@Y+@62k91|`8w5r66#c*UByY9?Bb40jUk!ZAtMS2dX z?ykCIewr|{!UsshiNnA`m3e!r+Eet`0go_h=RxRi zF{pr^4IkW+8mkY+BaAE$irIsNQkBG4ABjg8b;x+2=|4o7yQ@A7j|@YRdN5eR`b&q< z1o5LAJ0fb-uHb5g#}cpE41gQvC3cVZ$-w^1iyNIc0SGZ3nTy$pVa|RPJ_&X;UAcQu L=&FBEn7jWEZ=xHP literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 21c8adada..fb8f96411 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -57,6 +57,17 @@ #include "Magnum/Trade/ImageData.h" #include "Magnum/Trade/MeshData.h" +#ifndef MAGNUM_TARGET_GLES2 +#include + +#include "Magnum/GL/MeshView.h" +#include "Magnum/MeshTools/Concatenate.h" +#include "Magnum/MeshTools/GenerateIndices.h" +#include "Magnum/Primitives/Cone.h" +#include "Magnum/Shaders/Generic.h" +#include "Magnum/Shaders/Phong.h" +#endif + #include "configure.h" namespace Magnum { namespace Shaders { namespace Test { namespace { @@ -65,52 +76,79 @@ struct PhongGLTest: GL::OpenGLTester { explicit PhongGLTest(); void construct(); + #ifndef MAGNUM_TARGET_GLES2 + void constructUniformBuffers(); + #endif void constructMove(); + #ifndef MAGNUM_TARGET_GLES2 + void constructMoveUniformBuffers(); + #endif void constructTextureTransformationNotTextured(); + #ifndef MAGNUM_TARGET_GLES2 + void constructUniformBuffersInvalid(); + #endif + #ifndef MAGNUM_TARGET_GLES2 + void setUniformUniformBuffersEnabled(); + void bindBufferUniformBuffersNotEnabled(); + #endif void bindTexturesNotEnabled(); void setAlphaMaskNotEnabled(); void setTextureMatrixNotEnabled(); #ifndef MAGNUM_TARGET_GLES2 + void bindTextureTransformBufferNotEnabled(); + #endif + #ifndef MAGNUM_TARGET_GLES2 void setObjectIdNotEnabled(); #endif void setWrongLightCount(); void setWrongLightId(); + #ifndef MAGNUM_TARGET_GLES2 + void setWrongDrawOffset(); + #endif void renderSetup(); void renderTeardown(); - void renderDefaults(); - void renderColored(); - void renderSinglePixelTextured(); + template void renderDefaults(); + template void renderColored(); + template void renderSinglePixelTextured(); - void renderTextured(); - void renderTexturedNormal(); + template void renderTextured(); + template void renderTexturedNormal(); - template void renderVertexColor(); + template void renderVertexColor(); - void renderShininess(); + template void renderShininess(); void renderAlphaSetup(); void renderAlphaTeardown(); - void renderAlpha(); + template void renderAlpha(); #ifndef MAGNUM_TARGET_GLES2 void renderObjectIdSetup(); void renderObjectIdTeardown(); - void renderObjectId(); + template void renderObjectId(); #endif - void renderLights(); + template void renderLights(); + + /* This tests something that's irrelevant to UBOs */ void renderLightsSetOneByOne(); + /* This tests just the algorithm, not affected by UBOs */ void renderLowLightAngle(); - void renderZeroLights(); - void renderInstanced(); + template void renderZeroLights(); + + template void renderInstanced(); + + #ifndef MAGNUM_TARGET_GLES2 + void renderMulti(); + #endif private: PluginManager::Manager _manager{"nonexistent"}; @@ -172,6 +210,37 @@ constexpr struct { {"instanced normal texture offset", PhongGL::Flag::NormalTexture|PhongGL::Flag::InstancedTextureOffset, 3} }; +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + PhongGL::Flags flags; + UnsignedInt lightCount, materialCount, drawCount; +} ConstructUniformBuffersData[]{ + {"classic fallback", {}, 1, 1, 1}, + {"", PhongGL::Flag::UniformBuffers, 1, 1, 1}, + {"multiple lights, materials, draws", PhongGL::Flag::UniformBuffers, 30, 64, 128}, + {"zero lights", PhongGL::Flag::UniformBuffers, 0, 64, 128}, + {"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}, + {"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} +}; + +constexpr struct { + const char* name; + PhongGL::Flags flags; + UnsignedInt lightCount, materialCount, drawCount; + const char* message; +} ConstructUniformBuffersInvalidData[]{ + {"zero draws", PhongGL::Flag::UniformBuffers, 1, 1, 0, + "draw count can't be zero"}, + {"zero materials", PhongGL::Flag::UniformBuffers, 1, 0, 1, + "material count can't be zero"}, +}; +#endif + using namespace Math::Literals; const struct { @@ -483,69 +552,171 @@ constexpr struct { } }; +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + const char* expected; + PhongGL::Flags flags; + UnsignedInt lightCount, materialCount, drawCount; + UnsignedInt uniformIncrement; + Float maxThreshold, meanThreshold; +} RenderMultiData[] { + {"bind with offset, colored", "multidraw.tga", + {}, + 2, 1, 1, 16, 0.0f, 0.0f}, + {"bind with offset, textured", "multidraw-textured.tga", + PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture, + 2, 1, 1, 16, 0.0f, 0.0f}, + {"draw offset, colored", "multidraw.tga", + {}, + 4, 2, 3, 1, 0.0f, 0.0f}, + {"draw offset, textured", "multidraw-textured.tga", + PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture, + 4, 2, 3, 1, 0.0f, 0.0f} +}; +#endif + PhongGLTest::PhongGLTest() { - addInstancedTests({&PhongGLTest::construct}, Containers::arraySize(ConstructData)); + addInstancedTests({&PhongGLTest::construct}, + Containers::arraySize(ConstructData)); + + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({&PhongGLTest::constructUniformBuffers}, + Containers::arraySize(ConstructUniformBuffersData)); + #endif - addTests({&PhongGLTest::constructMove, + addTests({ + &PhongGLTest::constructMove, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::constructMoveUniformBuffers, + #endif - &PhongGLTest::constructTextureTransformationNotTextured, + &PhongGLTest::constructTextureTransformationNotTextured}); - &PhongGLTest::bindTexturesNotEnabled, - &PhongGLTest::setAlphaMaskNotEnabled, - &PhongGLTest::setTextureMatrixNotEnabled, - #ifndef MAGNUM_TARGET_GLES2 - &PhongGLTest::setObjectIdNotEnabled, - #endif - &PhongGLTest::setWrongLightCount, - &PhongGLTest::setWrongLightId}); + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({ + &PhongGLTest::constructUniformBuffersInvalid}, + Containers::arraySize(ConstructUniformBuffersInvalidData)); + #endif + + addTests({ + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::setUniformUniformBuffersEnabled, + &PhongGLTest::bindBufferUniformBuffersNotEnabled, + #endif + &PhongGLTest::bindTexturesNotEnabled, + &PhongGLTest::setAlphaMaskNotEnabled, + &PhongGLTest::setTextureMatrixNotEnabled, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::bindTextureTransformBufferNotEnabled, + #endif + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::setObjectIdNotEnabled, + #endif + &PhongGLTest::setWrongLightCount, + &PhongGLTest::setWrongLightId, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::setWrongDrawOffset + #endif + }); - addTests({&PhongGLTest::renderDefaults}, + addTests({ + &PhongGLTest::renderDefaults, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::renderDefaults + #endif + }, &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addInstancedTests({&PhongGLTest::renderColored}, + addInstancedTests({ + &PhongGLTest::renderColored, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::renderColored + #endif + }, Containers::arraySize(RenderColoredData), &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addInstancedTests({&PhongGLTest::renderSinglePixelTextured}, + addInstancedTests({ + &PhongGLTest::renderSinglePixelTextured, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::renderSinglePixelTextured + #endif + }, Containers::arraySize(RenderSinglePixelTexturedData), &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addInstancedTests({&PhongGLTest::renderTextured}, + addInstancedTests({ + &PhongGLTest::renderTextured, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::renderTextured + #endif + }, Containers::arraySize(RenderTexturedData), &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addInstancedTests({&PhongGLTest::renderTexturedNormal}, + addInstancedTests({ + &PhongGLTest::renderTexturedNormal, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::renderTexturedNormal + #endif + }, Containers::arraySize(RenderTexturedNormalData), &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addTests({&PhongGLTest::renderVertexColor, - &PhongGLTest::renderVertexColor}, + addTests({ + &PhongGLTest::renderVertexColor, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::renderVertexColor, + #endif + &PhongGLTest::renderVertexColor, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::renderVertexColor, + #endif + }, &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addInstancedTests({&PhongGLTest::renderShininess}, + addInstancedTests({ + &PhongGLTest::renderShininess, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::renderShininess, + #endif + }, Containers::arraySize(RenderShininessData), &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addInstancedTests({&PhongGLTest::renderAlpha}, + addInstancedTests({ + &PhongGLTest::renderAlpha, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::renderAlpha + #endif + }, Containers::arraySize(RenderAlphaData), &PhongGLTest::renderAlphaSetup, &PhongGLTest::renderAlphaTeardown); #ifndef MAGNUM_TARGET_GLES2 - addInstancedTests({&PhongGLTest::renderObjectId}, + addInstancedTests({ + &PhongGLTest::renderObjectId, + &PhongGLTest::renderObjectId}, Containers::arraySize(RenderObjectIdData), &PhongGLTest::renderObjectIdSetup, &PhongGLTest::renderObjectIdTeardown); #endif - addInstancedTests({&PhongGLTest::renderLights}, + addInstancedTests({ + &PhongGLTest::renderLights, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::renderLights, + #endif + }, Containers::arraySize(RenderLightsData), &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); @@ -555,7 +726,12 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addTests({&PhongGLTest::renderZeroLights}, + addTests({ + &PhongGLTest::renderZeroLights, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::renderZeroLights + #endif + }, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderObjectIdSetup, &PhongGLTest::renderObjectIdTeardown @@ -565,11 +741,23 @@ PhongGLTest::PhongGLTest() { #endif ); - addInstancedTests({&PhongGLTest::renderInstanced}, + addInstancedTests({ + &PhongGLTest::renderInstanced, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::renderInstanced, + #endif + }, Containers::arraySize(RenderInstancedData), &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({&PhongGLTest::renderMulti}, + Containers::arraySize(RenderMultiData), + &PhongGLTest::renderObjectIdSetup, + &PhongGLTest::renderObjectIdTeardown); + #endif + /* 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 @@ -617,6 +805,35 @@ void PhongGLTest::construct() { MAGNUM_VERIFY_NO_GL_ERROR(); } +#ifndef MAGNUM_TARGET_GLES2 +void PhongGLTest::constructUniformBuffers() { + auto&& data = ConstructUniformBuffersData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if((data.flags & PhongGL::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported()) + 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."); + #endif + + PhongGL shader{data.flags, data.lightCount, data.materialCount, data.drawCount}; + CORRADE_COMPARE(shader.flags(), data.flags); + CORRADE_COMPARE(shader.lightCount(), data.lightCount); + CORRADE_COMPARE(shader.materialCount(), data.materialCount); + CORRADE_COMPARE(shader.drawCount(), data.drawCount); + CORRADE_VERIFY(shader.id()); + { + #ifdef CORRADE_TARGET_APPLE + CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); + #endif + CORRADE_VERIFY(shader.validate().first); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); +} +#endif + void PhongGLTest::constructMove() { PhongGL a{PhongGL::Flag::AlphaMask, 3}; const GLuint id = a.id(); @@ -638,6 +855,38 @@ void PhongGLTest::constructMove() { CORRADE_VERIFY(!b.id()); } +#ifndef MAGNUM_TARGET_GLES2 +void PhongGLTest::constructMoveUniformBuffers() { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + PhongGL a{PhongGL::Flag::UniformBuffers, 3, 2, 5}; + const GLuint id = a.id(); + CORRADE_VERIFY(id); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + PhongGL b{std::move(a)}; + CORRADE_COMPARE(b.id(), id); + CORRADE_COMPARE(b.flags(), PhongGL::Flag::UniformBuffers); + CORRADE_COMPARE(b.lightCount(), 3); + CORRADE_COMPARE(b.materialCount(), 2); + CORRADE_COMPARE(b.drawCount(), 5); + CORRADE_VERIFY(!a.id()); + + PhongGL c{NoCreate}; + c = std::move(b); + CORRADE_COMPARE(c.id(), id); + CORRADE_COMPARE(c.flags(), PhongGL::Flag::UniformBuffers); + CORRADE_COMPARE(c.lightCount(), 3); + CORRADE_COMPARE(c.materialCount(), 2); + CORRADE_COMPARE(c.drawCount(), 5); + CORRADE_VERIFY(!b.id()); +} +#endif + void PhongGLTest::constructTextureTransformationNotTextured() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -650,6 +899,124 @@ void PhongGLTest::constructTextureTransformationNotTextured() { "Shaders::PhongGL: texture transformation enabled but the shader is not textured\n"); } +#ifndef MAGNUM_TARGET_GLES2 +void PhongGLTest::constructUniformBuffersInvalid() { + auto&& data = ConstructUniformBuffersInvalidData[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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + PhongGL{data.flags, data.lightCount, data.materialCount, data.drawCount}; + CORRADE_COMPARE(out.str(), Utility::formatString( + "Shaders::PhongGL: {}\n", data.message)); +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +void PhongGLTest::setUniformUniformBuffersEnabled() { + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + PhongGL shader{PhongGL::Flag::UniformBuffers}; + shader.setAmbientColor({}) + .setDiffuseColor({}) + .setNormalTextureScale({}) + .setSpecularColor({}) + .setShininess({}) + .setAlphaMask({}) + .setObjectId({}) + .setTransformationMatrix({}) + .setNormalMatrix({}) + .setProjectionMatrix({}) + .setTextureMatrix({}) + .setLightPositions(std::initializer_list{}) + .setLightPosition(0, Vector4{}) + .setLightColors(std::initializer_list{}) + .setLightColor(0, Color3{}) + .setLightSpecularColors({}) + .setLightSpecularColor(0, {}) + .setLightRanges({}) + .setLightRange(0, {}); + CORRADE_COMPARE(out.str(), + "Shaders::PhongGL::setAmbientColor(): the shader was created with uniform buffers enabled\n" + "Shaders::PhongGL::setDiffuseColor(): the shader was created with uniform buffers enabled\n" + "Shaders::PhongGL::setNormalTextureScale(): the shader was created with uniform buffers enabled\n" + "Shaders::PhongGL::setSpecularColor(): the shader was created with uniform buffers enabled\n" + "Shaders::PhongGL::setShininess(): the shader was created with uniform buffers enabled\n" + "Shaders::PhongGL::setAlphaMask(): the shader was created with uniform buffers enabled\n" + "Shaders::PhongGL::setObjectId(): the shader was created with uniform buffers enabled\n" + "Shaders::PhongGL::setTransformationMatrix(): the shader was created with uniform buffers enabled\n" + "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::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" + "Shaders::PhongGL::setLightColor(): the shader was created with uniform buffers enabled\n" + "Shaders::PhongGL::setLightSpecularColors(): the shader was created with uniform buffers enabled\n" + "Shaders::PhongGL::setLightSpecularColor(): the shader was created with uniform buffers enabled\n" + "Shaders::PhongGL::setLightRanges(): the shader was created with uniform buffers enabled\n" + "Shaders::PhongGL::setLightRange(): the shader was created with uniform buffers enabled\n"); +} + +void PhongGLTest::bindBufferUniformBuffersNotEnabled() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + GL::Buffer buffer; + PhongGL shader; + shader.bindProjectionBuffer(buffer) + .bindProjectionBuffer(buffer, 0, 16) + .bindTransformationBuffer(buffer) + .bindTransformationBuffer(buffer, 0, 16) + .bindDrawBuffer(buffer) + .bindDrawBuffer(buffer, 0, 16) + .bindTextureTransformationBuffer(buffer) + .bindTextureTransformationBuffer(buffer, 0, 16) + .bindMaterialBuffer(buffer) + .bindMaterialBuffer(buffer, 0, 16) + .bindLightBuffer(buffer) + .bindLightBuffer(buffer, 0, 16) + .setDrawOffset(0); + CORRADE_COMPARE(out.str(), + "Shaders::PhongGL::bindProjectionBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::PhongGL::bindProjectionBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::PhongGL::bindTransformationBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::PhongGL::bindTransformationBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::PhongGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::PhongGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::PhongGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::PhongGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::PhongGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::PhongGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::PhongGL::bindLightBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::PhongGL::bindLightBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::PhongGL::setDrawOffset(): the shader was not created with uniform buffers enabled\n"); +} +#endif + void PhongGLTest::bindTexturesNotEnabled() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -706,6 +1073,30 @@ void PhongGLTest::setTextureMatrixNotEnabled() { "Shaders::PhongGL::setTextureMatrix(): the shader was not created with texture transformation enabled\n"); } +#ifndef MAGNUM_TARGET_GLES2 +void PhongGLTest::bindTextureTransformBufferNotEnabled() { + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + GL::Buffer buffer{GL::Buffer::TargetHint::Uniform}; + PhongGL shader{PhongGL::Flag::UniformBuffers}; + shader.bindTextureTransformationBuffer(buffer) + .bindTextureTransformationBuffer(buffer, 0, 16); + CORRADE_COMPARE(out.str(), + "Shaders::PhongGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled\n" + "Shaders::PhongGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled\n"); +} +#endif + #ifndef MAGNUM_TARGET_GLES2 void PhongGLTest::setObjectIdNotEnabled() { #ifdef CORRADE_NO_ASSERT @@ -757,6 +1148,26 @@ void PhongGLTest::setWrongLightId() { "Shaders::PhongGL::setLightRange(): light ID 3 is out of bounds for 3 lights\n"); } +#ifndef MAGNUM_TARGET_GLES2 +void PhongGLTest::setWrongDrawOffset() { + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + PhongGL{PhongGL::Flag::UniformBuffers, 1, 2, 5} + .setDrawOffset(5); + CORRADE_COMPARE(out.str(), + "Shaders::PhongGL::setDrawOffset(): draw offset 5 is out of bounds for 5 draws\n"); +} +#endif + constexpr Vector2i RenderSize{80, 80}; void PhongGLTest::renderSetup() { @@ -785,11 +1196,52 @@ void PhongGLTest::renderTeardown() { _color = GL::Renderbuffer{NoCreate}; } -void PhongGLTest::renderDefaults() { +template void PhongGLTest::renderDefaults() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == PhongGL::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); - PhongGL{} - .draw(sphere); + PhongGL shader{flag}; + + if(flag == PhongGL::Flag{}) { + shader.draw(sphere); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == PhongGL::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{} + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + PhongDrawUniform{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + PhongMaterialUniform{} + }}; + GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, { + PhongLightUniform{} + }}; + shader + .bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .bindLightBuffer(lightUniform) + .draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -812,24 +1264,79 @@ void PhongGLTest::renderDefaults() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void PhongGLTest::renderColored() { +template void PhongGLTest::renderColored() { auto&& data = RenderColoredData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == PhongGL::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); - PhongGL{{}, 2} - .setLightColors({data.lightColor1, data.lightColor2}) - .setLightPositions({{data.lightPosition1, -3.0f, 2.0f, 0.0f}, - {data.lightPosition2, -3.0f, 2.0f, 0.0f}}) - .setAmbientColor(0x330033_rgbf) - .setDiffuseColor(0xccffcc_rgbf) - .setSpecularColor(0x6666ff_rgbf) - .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(data.rotation)) - .setNormalMatrix(Matrix4::rotationY(data.rotation).normalMatrix()) - .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) - .draw(sphere); + PhongGL shader{flag, 2}; + + if(flag == PhongGL::Flag{}) { + shader + .setLightColors({data.lightColor1, data.lightColor2}) + .setLightPositions({{data.lightPosition1, -3.0f, 2.0f, 0.0f}, + {data.lightPosition2, -3.0f, 2.0f, 0.0f}}) + .setAmbientColor(0x330033_rgbf) + .setDiffuseColor(0xccffcc_rgbf) + .setSpecularColor(0x6666ff_rgbf) + .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(data.rotation)) + .setNormalMatrix(Matrix4::rotationY(data.rotation).normalMatrix()) + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + .draw(sphere); + + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == PhongGL::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{} + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{} + .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(data.rotation)) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + PhongDrawUniform{} + .setNormalMatrix(Matrix4::rotationY(data.rotation).normalMatrix()) + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + PhongMaterialUniform{} + .setAmbientColor(0x330033_rgbf) + .setDiffuseColor(0xccffcc_rgbf) + .setSpecularColor(0x6666ff_rgbf) + }}; + GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, { + PhongLightUniform{} + .setPosition({data.lightPosition1, -3.0f, 2.0f, 0.0f}) + .setColor(data.lightColor1), + PhongLightUniform{} + .setPosition({data.lightPosition2, -3.0f, 2.0f, 0.0f}) + .setColor(data.lightColor2) + }}; + shader + .bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .bindLightBuffer(lightUniform) + .draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -867,10 +1374,21 @@ constexpr GL::TextureFormat TextureFormatRGBA = #endif ; -void PhongGLTest::renderSinglePixelTextured() { +template void PhongGLTest::renderSinglePixelTextured() { auto&& data = RenderSinglePixelTexturedData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == PhongGL::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, Primitives::UVSphereFlag::TextureCoordinates)); @@ -901,13 +1419,7 @@ void PhongGLTest::renderSinglePixelTextured() { .setStorage(1, TextureFormatRGBA, Vector2i{1}) .setSubImage(0, {}, specularImage); - PhongGL shader{PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture, 2}; - shader.setLightColors({0x993366_rgbf, 0x669933_rgbf}) - .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, - { 3.0f, -3.0f, 2.0f, 0.0f}}) - .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f))) - .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)); - + PhongGL shader{PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|flag, 2}; if(data.multiBind) shader.bindTextures(&ambient, &diffuse, &specular, nullptr); else shader @@ -915,7 +1427,51 @@ void PhongGLTest::renderSinglePixelTextured() { .bindDiffuseTexture(diffuse) .bindSpecularTexture(specular); - shader.draw(sphere); + if(flag == PhongGL::Flag{}) { + shader.setLightColors({0x993366_rgbf, 0x669933_rgbf}) + .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, + { 3.0f, -3.0f, 2.0f, 0.0f}}) + .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f))) + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + .draw(sphere); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == PhongGL::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{} + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{} + .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f))) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + PhongDrawUniform{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + PhongMaterialUniform{} + /* Has to be set because the default is black regardless of + whether the texture is present or not (it has no way to + know) */ + .setAmbientColor(0xffffff_rgbf) + }}; + GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, { + PhongLightUniform{} + .setPosition({-3.0f, -3.0f, 2.0f, 0.0f}) + .setColor(0x993366_rgbf), + PhongLightUniform{} + .setPosition({ 3.0f, -3.0f, 2.0f, 0.0f}) + .setColor(0x669933_rgbf) + }}; + shader.bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .bindLightBuffer(lightUniform) + .draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -938,10 +1494,21 @@ void PhongGLTest::renderSinglePixelTextured() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void PhongGLTest::renderTextured() { +template void PhongGLTest::renderTextured() { auto&& data = RenderTexturedData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == PhongGL::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -949,10 +1516,7 @@ void PhongGLTest::renderTextured() { GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, Primitives::UVSphereFlag::TextureCoordinates)); - PhongGL shader{data.flags, 2}; - - if(data.textureTransformation != Matrix3{}) - shader.setTextureMatrix(data.textureTransformation); + PhongGL shader{data.flags|flag, 2}; Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); CORRADE_VERIFY(importer); @@ -966,11 +1530,7 @@ void PhongGLTest::renderTextured() { .setWrapping(GL::SamplerWrapping::ClampToEdge) .setStorage(1, TextureFormatRGB, image->size()) .setSubImage(0, {}, *image); - shader - .bindAmbientTexture(ambient) - /* Colorized. Case without a color (where it should be white) is - tested in renderSinglePixelTextured() */ - .setAmbientColor(0xff9999_rgbf); + shader.bindAmbientTexture(ambient); } /* If no diffuse texture is present, dial down the default diffuse color @@ -984,12 +1544,8 @@ void PhongGLTest::renderTextured() { .setWrapping(GL::SamplerWrapping::ClampToEdge) .setStorage(1, TextureFormatRGB, image->size()) .setSubImage(0, {}, *image); - shader - .bindDiffuseTexture(diffuse) - /* Colorized. Case without a color (where it should be white) is - tested in renderSinglePixelTextured() */ - .setDiffuseColor(0x9999ff_rgbf); - } else shader.setDiffuseColor(0x333333_rgbf); + shader.bindDiffuseTexture(diffuse); + } GL::Texture2D specular; if(data.flags & PhongGL::Flag::SpecularTexture) { @@ -1000,25 +1556,90 @@ void PhongGLTest::renderTextured() { .setWrapping(GL::SamplerWrapping::ClampToEdge) .setStorage(1, TextureFormatRGB, image->size()) .setSubImage(0, {}, *image); - shader - .bindSpecularTexture(specular) + shader.bindSpecularTexture(specular); + } + + if(flag == PhongGL::Flag{}) { + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); + if(data.flags & PhongGL::Flag::AmbientTexture) + /* Colorized. Case without a color (where it should be white) is + tested in renderSinglePixelTextured() */ + shader.setAmbientColor(0xff9999_rgbf); + if(data.flags & PhongGL::Flag::DiffuseTexture) + /* Colorized. Case without a color (where it should be white) is + tested in renderSinglePixelTextured() */ + shader.setDiffuseColor(0x9999ff_rgbf); + else shader.setDiffuseColor(0x333333_rgbf); + if(data.flags & PhongGL::Flag::SpecularTexture) /* Colorized. Case without a color (where it should be white) is tested in renderSinglePixelTextured() */ - .setSpecularColor(0x99ff99_rgbf); + shader.setSpecularColor(0x99ff99_rgbf); + + /* Using default (white) light colors to have the texture data visible + better */ + shader.setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, + { 3.0f, -3.0f, 2.0f, 0.0f}}) + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)) + .setNormalMatrix((Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)).normalMatrix()) + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + .draw(sphere); } - - /* Using default (white) light colors to have the texture data visible - better */ - shader.setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, - { 3.0f, -3.0f, 2.0f, 0.0f}}) - .setTransformationMatrix( - Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)) - .setNormalMatrix((Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)).normalMatrix()) - .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) - .draw(sphere); + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == PhongGL::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{}.setProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) + ) + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{}.setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + PhongDrawUniform{}.setNormalMatrix( + (Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)).normalMatrix() + ) + }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setTextureMatrix(data.textureTransformation) + }}; + GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, { + PhongLightUniform{}.setPosition({-3.0f, -3.0f, 2.0f, 0.0f}), + PhongLightUniform{}.setPosition({3.0f, -3.0f, 2.0f, 0.0f}) + }}; + + PhongMaterialUniform materialUniformData[1]; + if(data.flags & PhongGL::Flag::AmbientTexture) + materialUniformData->setAmbientColor(0xff9999_rgbf); + if(data.flags & PhongGL::Flag::DiffuseTexture) + materialUniformData->setDiffuseColor(0x9999ff_rgbf); + else + materialUniformData->setDiffuseColor(0x333333_rgbf); + if(data.flags & PhongGL::Flag::SpecularTexture) + materialUniformData->setSpecularColor(0x99ff99_rgbf); + GL::Buffer materialUniform{materialUniformData}; + + if(data.textureTransformation != Matrix3{}) + shader.bindTextureTransformationBuffer(textureTransformationUniform); + shader.bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .bindLightBuffer(lightUniform) + .draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1037,10 +1658,21 @@ void PhongGLTest::renderTextured() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void PhongGLTest::renderTexturedNormal() { +template void PhongGLTest::renderTexturedNormal() { auto&& data = RenderTexturedNormalData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == PhongGL::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -1081,30 +1713,71 @@ 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, 2}; - shader.setLightPositions({ - Matrix4::rotationZ(data.rotation)*Vector4{-3.0f, -3.0f, 2.0f, 0.0f}, - Matrix4::rotationZ(data.rotation)*Vector4{ 3.0f, -3.0f, 2.0f, 0.0f}}) - .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.35f))* - Matrix4::rotationZ(data.rotation)* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)) - .setNormalMatrix((Matrix4::rotationZ(data.rotation)* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)).normalMatrix()) - .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) - .setDiffuseColor(0x999999_rgbf); - - /* Verify the default is working properly */ - if(data.scale != 1.0f) - shader.setNormalTextureScale(data.scale); - + PhongGL shader{PhongGL::Flag::NormalTexture|data.flags|flag, 2}; if(data.multiBind) shader.bindTextures(nullptr, nullptr, nullptr, &normal); else shader.bindNormalTexture(normal); - shader.draw(plane); + if(flag == PhongGL::Flag{}) { + /* Verify the default is working properly */ + if(data.scale != 1.0f) + shader.setNormalTextureScale(data.scale); + + shader.setLightPositions({ + Matrix4::rotationZ(data.rotation)*Vector4{-3.0f, -3.0f, 2.0f, 0.0f}, + Matrix4::rotationZ(data.rotation)*Vector4{ 3.0f, -3.0f, 2.0f, 0.0f}}) + .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.35f))* + Matrix4::rotationZ(data.rotation)* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)) + .setNormalMatrix((Matrix4::rotationZ(data.rotation)* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)).normalMatrix()) + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + .setDiffuseColor(0x999999_rgbf) + .draw(plane); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == PhongGL::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{}.setProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) + ) + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{}.setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.35f))* + Matrix4::rotationZ(data.rotation)* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + PhongDrawUniform{}.setNormalMatrix( + (Matrix4::rotationZ(data.rotation)* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)).normalMatrix() + ) + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + PhongMaterialUniform{} + .setDiffuseColor(0x999999_rgbf) + .setNormalTextureScale(data.scale) + }}; + 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}) + }}; + shader.bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .bindLightBuffer(lightUniform) + .draw(plane); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1137,8 +1810,20 @@ void PhongGLTest::renderTexturedNormal() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -template void PhongGLTest::renderVertexColor() { - setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); +template void PhongGLTest::renderVertexColor() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == PhongGL::Flag::UniformBuffers) { + setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"}); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } else + #endif + { + setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); + } if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) @@ -1171,20 +1856,62 @@ template void PhongGLTest::renderVertexColor() { .setStorage(1, TextureFormatRGB, image->size()) .setSubImage(0, {}, *image); - PhongGL{PhongGL::Flag::DiffuseTexture|PhongGL::Flag::VertexColor, 2} - .setLightPositions({{-3.0f, -3.0f, 0.0f, 0.0f}, - { 3.0f, -3.0f, 0.0f, 0.0f}}) - .setTransformationMatrix( - Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)) - .setNormalMatrix((Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)).normalMatrix()) - .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) - .setAmbientColor(0x111111_rgbf) - .setDiffuseColor(0x9999ff_rgbf) - .bindDiffuseTexture(diffuse) - .draw(sphere); + PhongGL shader{PhongGL::Flag::DiffuseTexture|PhongGL::Flag::VertexColor|flag, 2}; + shader.bindDiffuseTexture(diffuse); + + if(flag == PhongGL::Flag{}) { + shader + .setLightPositions({{-3.0f, -3.0f, 0.0f, 0.0f}, + { 3.0f, -3.0f, 0.0f, 0.0f}}) + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)) + .setNormalMatrix((Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)).normalMatrix()) + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + .setAmbientColor(0x111111_rgbf) + .setDiffuseColor(0x9999ff_rgbf) + .draw(sphere); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == PhongGL::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{}.setProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) + ) + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{}.setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + PhongDrawUniform{}.setNormalMatrix( + (Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)).normalMatrix() + ) + }}; + GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, { + PhongLightUniform{}.setPosition({-3.0f, -3.0f, 0.0f, 0.0f}), + PhongLightUniform{}.setPosition({ 3.0f, -3.0f, 0.0f, 0.0f}) + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + PhongMaterialUniform{} + .setAmbientColor(0x111111_rgbf) + .setDiffuseColor(0x9999ff_rgbf) + }}; + shader.bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .bindLightBuffer(lightUniform) + .draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1203,20 +1930,68 @@ template void PhongGLTest::renderVertexColor() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void PhongGLTest::renderShininess() { +template void PhongGLTest::renderShininess() { auto&& data = RenderShininessData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == PhongGL::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); - PhongGL{} - .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}}) - .setDiffuseColor(0xff3333_rgbf) - .setSpecularColor(data.specular) - .setShininess(data.shininess) - .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f))) - .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) - .draw(sphere); + PhongGL shader{flag}; + if(flag == PhongGL::Flag{}) { + shader + .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}}) + .setDiffuseColor(0xff3333_rgbf) + .setSpecularColor(data.specular) + .setShininess(data.shininess) + .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f))) + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + .draw(sphere); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == PhongGL::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{}.setProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) + ) + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{}.setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f)) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + PhongDrawUniform{} + }}; + GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, { + PhongLightUniform{}.setPosition({-3.0f, -3.0f, 2.0f, 0.0f}) + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + PhongMaterialUniform{} + .setDiffuseColor(0xff3333_rgbf) + .setSpecularColor(data.specular) + .setShininess(data.shininess) + }}; + shader + .bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .bindLightBuffer(lightUniform) + .draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1302,10 +2077,21 @@ void PhongGLTest::renderAlphaTeardown() { renderTeardown(); } -void PhongGLTest::renderAlpha() { +template void PhongGLTest::renderAlpha() { auto&& data = RenderAlphaData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == PhongGL::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -1343,31 +2129,79 @@ void PhongGLTest::renderAlpha() { GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, Primitives::UVSphereFlag::TextureCoordinates)); - PhongGL shader{data.flags, 2}; - shader.setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, - { 3.0f, -3.0f, 2.0f, 0.0f}}) - .setTransformationMatrix( - Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)) - .setNormalMatrix((Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)).normalMatrix()) - .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) - .setAmbientColor(data.ambientColor) - .setDiffuseColor(data.diffuseColor) - .setSpecularColor(0xffffff00_rgbaf) - .bindTextures(&ambient, &diffuse, nullptr, nullptr); - - /* Test that the default is correct by not setting the threshold if it's - equal to the default */ - if(data.flags & PhongGL::Flag::AlphaMask && data.threshold != 0.5f) - shader.setAlphaMask(data.threshold); - - /* For proper Z order draw back faces first and then front faces */ - GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Front); - shader.draw(sphere); - GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Back); - shader.draw(sphere); + PhongGL shader{data.flags|flag, 2}; + shader.bindTextures(&ambient, &diffuse, nullptr, nullptr); + + if(flag == PhongGL::Flag{}) { + shader.setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, + { 3.0f, -3.0f, 2.0f, 0.0f}}) + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)) + .setNormalMatrix((Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)).normalMatrix()) + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + .setAmbientColor(data.ambientColor) + .setDiffuseColor(data.diffuseColor) + .setSpecularColor(0xffffff00_rgbaf); + + /* Test that the default is correct by not setting the threshold if + it's equal to the default */ + if(data.flags & PhongGL::Flag::AlphaMask && data.threshold != 0.5f) + shader.setAlphaMask(data.threshold); + + /* For proper Z order draw back faces first and then front faces */ + GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Front); + shader.draw(sphere); + GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Back); + shader.draw(sphere); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == PhongGL::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{}.setProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) + ) + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{}.setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + PhongDrawUniform{}.setNormalMatrix( + (Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)).normalMatrix() + ) + }}; + GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, { + PhongLightUniform{}.setPosition({-3.0f, -3.0f, 2.0f, 0.0f}), + PhongLightUniform{}.setPosition({3.0f, -3.0f, 2.0f, 0.0f}) + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + PhongMaterialUniform{} + .setAmbientColor(data.ambientColor) + .setDiffuseColor(data.diffuseColor) + .setSpecularColor(0xffffff00_rgbaf) + .setAlphaMask(data.threshold) + }}; + shader.bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .bindLightBuffer(lightUniform); + + /* For proper Z order draw back faces first and then front faces */ + GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Front); + shader.draw(sphere); + GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Back); + shader.draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1426,10 +2260,21 @@ void PhongGLTest::renderObjectIdTeardown() { _framebuffer = GL::Framebuffer{NoCreate}; } -void PhongGLTest::renderObjectId() { +template void PhongGLTest::renderObjectId() { auto&& data = RenderObjectIdData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == PhongGL::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); @@ -1445,17 +2290,55 @@ void PhongGLTest::renderObjectId() { GL::Buffer{Containers::arrayView({11002u, 48823u})}, 1, 0, PhongGL::ObjectId{}); - PhongGL{data.flags, 2} - .setLightColors({0x993366_rgbf, 0x669933_rgbf}) - .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, - { 3.0f, -3.0f, 2.0f, 0.0f}}) - .setAmbientColor(0x330033_rgbf) - .setDiffuseColor(0xccffcc_rgbf) - .setSpecularColor(0x6666ff_rgbf) - .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f))) - .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) - .setObjectId(data.uniformId) - .draw(sphere); + PhongGL shader{data.flags|flag, 2}; + + if(flag == PhongGL::Flag{}) { + shader + .setLightColors({0x993366_rgbf, 0x669933_rgbf}) + .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, + { 3.0f, -3.0f, 2.0f, 0.0f}}) + .setAmbientColor(0x330033_rgbf) + .setDiffuseColor(0xccffcc_rgbf) + .setSpecularColor(0x6666ff_rgbf) + .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f))) + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + .setObjectId(data.uniformId) + .draw(sphere); + } else if(flag == PhongGL::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{}.setProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) + ) + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{}.setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f)) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + PhongDrawUniform{}.setObjectId(data.uniformId) + }}; + GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, { + PhongLightUniform{} + .setPosition({-3.0f, -3.0f, 2.0f, 0.0f}) + .setColor(0x993366_rgbf), + PhongLightUniform{} + .setPosition({3.0f, -3.0f, 2.0f, 0.0f}) + .setColor(0x669933_rgbf) + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + PhongMaterialUniform{} + .setAmbientColor(0x330033_rgbf) + .setDiffuseColor(0xccffcc_rgbf) + .setSpecularColor(0x6666ff_rgbf) + }}; + shader.bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .bindLightBuffer(lightUniform) + .draw(sphere); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1493,29 +2376,79 @@ void PhongGLTest::renderObjectId() { } #endif -void PhongGLTest::renderLights() { +template void PhongGLTest::renderLights() { auto&& data = RenderLightsData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == PhongGL::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + GL::Mesh plane = MeshTools::compile(Primitives::planeSolid()); Matrix4 transformation = Matrix4::translation({0.0f, 0.0f, -1.5f}); - PhongGL{{}, 1} - /* Set non-black ambient to catch accidental NaNs -- the render should - never be fully black */ - .setAmbientColor(0x222222_rgbf) - .setSpecularColor(data.specularColor) - .setLightPositions({data.position}) - .setLightColors({0xff8080_rgbf*data.intensity}) - .setLightSpecularColors({data.lightSpecularColor}) - .setLightRanges({data.range}) - .setShininess(60.0f) - .setTransformationMatrix(transformation) - .setNormalMatrix(transformation.normalMatrix()) - .setProjectionMatrix(Matrix4::perspectiveProjection(80.0_degf, 1.0f, 0.1f, 20.0f)) - .draw(plane); + PhongGL shader{flag, 1}; + if(flag == PhongGL::Flag{}) { + shader + /* Set non-black ambient to catch accidental NaNs -- the render + should never be fully black */ + .setAmbientColor(0x222222_rgbf) + .setSpecularColor(data.specularColor) + .setLightPositions({data.position}) + .setLightColors({0xff8080_rgbf*data.intensity}) + .setLightSpecularColors({data.lightSpecularColor}) + .setLightRanges({data.range}) + .setShininess(60.0f) + .setTransformationMatrix(transformation) + .setNormalMatrix(transformation.normalMatrix()) + .setProjectionMatrix(Matrix4::perspectiveProjection(80.0_degf, 1.0f, 0.1f, 20.0f)) + .draw(plane); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == PhongGL::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{}.setProjectionMatrix( + Matrix4::perspectiveProjection(80.0_degf, 1.0f, 0.1f, 20.0f) + ) + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{}.setTransformationMatrix(transformation) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + PhongDrawUniform{}.setNormalMatrix(transformation.normalMatrix()) + }}; + GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, { + PhongLightUniform{} + .setPosition({data.position}) + .setColor(0xff8080_rgbf*data.intensity) + .setSpecularColor(data.lightSpecularColor) + .setRange(data.range), + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + PhongMaterialUniform{} + .setAmbientColor(0x222222_rgbf) + .setSpecularColor(data.specularColor) + .setShininess(60.0f) + }}; + shader + .bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .bindLightBuffer(lightUniform) + .draw(plane); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1635,13 +2568,22 @@ void PhongGLTest::renderLowLightAngle() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void PhongGLTest::renderZeroLights() { - CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Draw), GL::Framebuffer::Status::Complete); - +template void PhongGLTest::renderZeroLights() { if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == PhongGL::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, Primitives::UVSphereFlag::TextureCoordinates)); @@ -1655,7 +2597,7 @@ void PhongGLTest::renderZeroLights() { flags |= PhongGL::Flag::ObjectId; } #endif - PhongGL shader{flags, 0}; + PhongGL shader{flags|flag, 0}; Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); CORRADE_VERIFY(importer); @@ -1669,40 +2611,89 @@ void PhongGLTest::renderZeroLights() { .setStorage(1, TextureFormatRGBA, ambientImage->size()) .setSubImage(0, {}, *ambientImage); - shader - .bindAmbientTexture(ambient) - .setAmbientColor(0x9999ff_rgbf) - .setTransformationMatrix( - Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)) - .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) - /* Keep alpha mask at the default 0.5 to test the default */ - /* Passing a zero-sized light position / color array, shouldn't assert */ - .setLightPositions(Containers::ArrayView{}) - .setLightColors(Containers::ArrayView{}) - /* Using a bogus normal matrix -- it's not used so it should be okay. - Same for all other unused values, they should get ignored. */ - .setNormalMatrix(Matrix3x3{Math::ZeroInit}) - .setDiffuseColor(0xfa9922_rgbf) - .setSpecularColor(0xfa9922_rgbf) - .setShininess(0.2f) - .setNormalTextureScale(-0.3f); + shader.bindAmbientTexture(ambient); + + if(flag == PhongGL::Flag{}) { + shader + .setAmbientColor(0x9999ff_rgbf) + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)) + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + /* Keep alpha mask at the default 0.5 to test the default */ + /* Passing a zero-sized light position / color array, shouldn't + assert */ + .setLightPositions(Containers::ArrayView{}) + .setLightColors(Containers::ArrayView{}) + /* Using a bogus normal matrix -- it's not used so it should be + okay. Same for all other unused values, they should get + ignored. */ + .setNormalMatrix(Matrix3x3{Math::ZeroInit}) + .setDiffuseColor(0xfa9922_rgbf) + .setSpecularColor(0xfa9922_rgbf) + .setShininess(0.2f) + .setNormalTextureScale(-0.3f); + + #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + #endif + { + shader.setObjectId(65534); + } + #endif + /* For proper Z order draw back faces first and then front faces */ + GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Front); + shader.draw(sphere); + GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Back); + shader.draw(sphere); + } #ifndef MAGNUM_TARGET_GLES2 - #ifndef MAGNUM_TARGET_GLES - if(GL::Context::current().isExtensionSupported()) - #endif - { - shader.setObjectId(65534); + else if(flag == PhongGL::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{}.setProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) + ) + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{}.setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + PhongDrawUniform{} + /* Using a bogus normal matrix -- it's not used so it should be + okay. */ + .setNormalMatrix(Matrix3x3{Math::ZeroInit}) + .setObjectId(65534) + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + PhongMaterialUniform{} + .setAmbientColor(0x9999ff_rgbf) + /* Same for all other unused values, they should get ignored */ + .setDiffuseColor(0xfa9922_rgbf) + .setSpecularColor(0xfa9922_rgbf) + .setShininess(0.2f) + .setNormalTextureScale(-0.3f) + }}; + /* Not binding any light buffer as it's not needed */ + shader.bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform); + + /* For proper Z order draw back faces first and then front faces */ + GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Front); + shader.draw(sphere); + GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Back); + shader.draw(sphere); } #endif - - /* For proper Z order draw back faces first and then front faces */ - GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Front); - shader.draw(sphere); - GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Back); - shader.draw(sphere); + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -1744,10 +2735,21 @@ void PhongGLTest::renderZeroLights() { #endif } -void PhongGLTest::renderInstanced() { +template void PhongGLTest::renderInstanced() { auto&& data = RenderInstancedData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == PhongGL::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::instanced_arrays::string() << "is not supported."); @@ -1823,27 +2825,71 @@ void PhongGLTest::renderInstanced() { PhongGL shader{PhongGL::Flag::DiffuseTexture| PhongGL::Flag::VertexColor| PhongGL::Flag::InstancedTransformation| - PhongGL::Flag::InstancedTextureOffset|data.flags, 2}; - shader - .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, - { 3.0f, -3.0f, 2.0f, 0.0f}}) - .setTransformationMatrix( - Matrix4::translation(Vector3::zAxis(-1.75f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)* - Matrix4::scaling(Vector3{0.4f})) - .setNormalMatrix((Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)).normalMatrix()) - .setProjectionMatrix( - Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) - .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) - .bindDiffuseTexture(diffuse) - .setDiffuseColor(0xffff99_rgbf); - + PhongGL::Flag::InstancedTextureOffset|data.flags|flag, 2}; + shader.bindDiffuseTexture(diffuse); if(data.flags & PhongGL::Flag::NormalTexture) shader.bindNormalTexture(normal); - shader.draw(sphere); + if(flag == PhongGL::Flag{}) { + shader + .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, + { 3.0f, -3.0f, 2.0f, 0.0f}}) + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-1.75f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)* + Matrix4::scaling(Vector3{0.4f})) + .setNormalMatrix((Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)).normalMatrix()) + .setProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) + .setDiffuseColor(0xffff99_rgbf) + .draw(sphere); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == PhongGL::Flag::UniformBuffers) { + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{}.setProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) + ) + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{}.setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-1.75f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)* + Matrix4::scaling(Vector3{0.4f}) + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + PhongDrawUniform{}.setNormalMatrix( + (Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)).normalMatrix() + ) + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + PhongMaterialUniform{} + .setDiffuseColor(0xffff99_rgbf) + }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) + }}; + GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, { + PhongLightUniform{}.setPosition(Vector4{-3.0f, -3.0f, 2.0f, 0.0f}), + PhongLightUniform{}.setPosition(Vector4{3.0f, -3.0f, 2.0f, 0.0f}) + }}; + shader.bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindTextureTransformationBuffer(textureTransformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .bindLightBuffer(lightUniform) + .draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); CORRADE_COMPARE_WITH( @@ -1853,6 +2899,279 @@ void PhongGLTest::renderInstanced() { (DebugTools::CompareImageToFile{_manager, data.maxThreshold, data.meanThreshold})); } +#ifndef MAGNUM_TARGET_GLES2 +void PhongGLTest::renderMulti() { + auto&& data = RenderMultiData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + GL::Texture2D diffuse; + if(data.flags & PhongGL::Flag::DiffuseTexture) { + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + 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); + } + + Trade::MeshData sphereData = Primitives::uvSphereSolid(16, 32, + Primitives::UVSphereFlag::TextureCoordinates| + Primitives::UVSphereFlag::Tangents); + /* Plane is a strip, make it indexed first */ + Trade::MeshData planeData = MeshTools::generateIndices(Primitives::planeSolid( + Primitives::PlaneFlag::TextureCoordinates| + Primitives::PlaneFlag::Tangents)); + Trade::MeshData coneData = Primitives::coneSolid(1, 32, 1.0f, + Primitives::ConeFlag::TextureCoordinates| + Primitives::ConeFlag::Tangents); + GL::Mesh mesh = MeshTools::compile(MeshTools::concatenate({sphereData, planeData, coneData})); + GL::MeshView sphere{mesh}; + sphere.setCount(sphereData.indexCount()); + GL::MeshView plane{mesh}; + plane.setCount(planeData.indexCount()) + .setIndexRange(sphereData.indexCount()); + GL::MeshView cone{mesh}; + cone.setCount(coneData.indexCount()) + .setIndexRange(sphereData.indexCount() + planeData.indexCount()); + + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{}.setProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f) + ) + }}; + + /* Some drivers have uniform offset alignment as high as 256, which means + the subsequent sets of uniforms have to be aligned to a multiply of it. + The data.uniformIncrement is set high enough to ensure that, in the + non-offset-bind case this value is 1. */ + + Containers::Array materialData{data.uniformIncrement + 1}; + materialData[0*data.uniformIncrement] = PhongMaterialUniform{} + .setDiffuseColor(data.flags & PhongGL::Flag::DiffuseTexture ? + 0xffffff_rgbf : 0x00ffff_rgbf); + materialData[1*data.uniformIncrement] = PhongMaterialUniform{} + .setDiffuseColor(data.flags & PhongGL::Flag::DiffuseTexture ? + 0xffffff_rgbf : 0xffff00_rgbf); + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, materialData}; + + /* The shader has two lights hardcoded, so make sure the buffer can fit + 2 items enough even though the last draw needs just one light. Not a + problem on desktop, but WebGL complains. */ + Containers::Array lightData{2*data.uniformIncrement + 2}; + lightData[0*data.uniformIncrement] = PhongLightUniform{} + .setPosition(Vector4{0.0f, 0.0f, 1.0f, 0.0f}) + .setColor(data.flags & PhongGL::Flag::DiffuseTexture ? + 0xffffff_rgbf : 0x00ffff_rgbf); + lightData[1*data.uniformIncrement + 0] = PhongLightUniform{} + .setPosition(Vector4{-3.0f, -3.0f, 2.0f, 0.0f}) + .setColor(0x999999_rgbf) + .setSpecularColor(0xff0000_rgbf); + lightData[1*data.uniformIncrement + 1] = PhongLightUniform{} + .setPosition(Vector4{3.0f, -3.0f, 2.0f, 0.0f}) + .setColor(0x999999_rgbf) + .setSpecularColor(0x00ff00_rgbf); + /* This will put the light to position 4 in case data.uniformIncrement is 1 + and to an offset aligned to 256 if it's higher */ + lightData[2*data.uniformIncrement + 1/data.uniformIncrement] = PhongLightUniform{} + .setPosition(Vector4{0.0f, 0.0f, 1.0f, 0.0f}) + .setColor(data.flags & PhongGL::Flag::DiffuseTexture ? + 0xffffff_rgbf : 0xff00ff_rgbf); + GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, lightData}; + + Containers::Array transformationData{2*data.uniformIncrement + 1}; + transformationData[0*data.uniformIncrement] = TransformationUniform3D{} + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({-1.25f, -1.25f, 0.0f})* + /* to test the normal matrix is applied properly */ + Matrix4::rotationX(90.0_degf) + ); + transformationData[1*data.uniformIncrement] = TransformationUniform3D{} + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({ 1.25f, -1.25f, 0.0f}) + ); + transformationData[2*data.uniformIncrement] = TransformationUniform3D{} + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({ 0.0f, 1.0f, 1.0f}) + ); + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, transformationData}; + + Containers::Array textureTransformationData{2*data.uniformIncrement + 1}; + textureTransformationData[0*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({0.0f, 0.0f}) + ); + textureTransformationData[1*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({1.0f, 0.0f}) + ); + textureTransformationData[2*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::scaling(Vector2{0.5f})* + Matrix3::translation({0.5f, 1.0f}) + ); + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; + + Containers::Array drawData{2*data.uniformIncrement + 1}; + /* Material / light offsets are zero if we have single draw, as those are + done with UBO offset bindings instead. */ + drawData[0*data.uniformIncrement] = PhongDrawUniform{} + .setMaterialId(data.drawCount == 1 ? 0 : 1) + .setLightOffsetCount(data.drawCount == 1 ? 0 : 1, 2) + .setNormalMatrix(transformationData[0*data.uniformIncrement].transformationMatrix.normalMatrix()) + .setObjectId(1211); + drawData[1*data.uniformIncrement] = PhongDrawUniform{} + .setMaterialId(data.drawCount == 1 ? 0 : 0) + .setLightOffsetCount(data.drawCount == 1 ? 0 : 3, 1) + .setNormalMatrix(transformationData[1*data.uniformIncrement].transformationMatrix.normalMatrix()) + .setObjectId(5627); + drawData[2*data.uniformIncrement] = PhongDrawUniform{} + .setMaterialId(data.drawCount == 1 ? 0 : 1) + .setLightOffsetCount(data.drawCount == 1 ? 0 : 0, 1) + .setNormalMatrix(transformationData[2*data.uniformIncrement].transformationMatrix.normalMatrix()) + .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) { + shader.bindMaterialBuffer(materialUniform, + 1*data.uniformIncrement*sizeof(PhongMaterialUniform), + sizeof(PhongMaterialUniform)); + shader.bindLightBuffer(lightUniform, + 1*data.uniformIncrement*sizeof(PhongLightUniform), + 2*sizeof(PhongLightUniform)); + shader.bindTransformationBuffer(transformationUniform, + 0*data.uniformIncrement*sizeof(TransformationUniform3D), + sizeof(TransformationUniform3D)); + shader.bindDrawBuffer(drawUniform, + 0*data.uniformIncrement*sizeof(PhongDrawUniform), + sizeof(PhongDrawUniform)); + if(data.flags & PhongGL::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 0*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(sphere); + + shader.bindMaterialBuffer(materialUniform, + 0*data.uniformIncrement*sizeof(PhongMaterialUniform), + sizeof(PhongMaterialUniform)); + shader.bindLightBuffer(lightUniform, + 2*data.uniformIncrement*sizeof(PhongLightUniform), + 2*sizeof(PhongLightUniform)); + shader.bindTransformationBuffer(transformationUniform, + 1*data.uniformIncrement*sizeof(TransformationUniform3D), + sizeof(TransformationUniform3D)); + shader.bindDrawBuffer(drawUniform, + 1*data.uniformIncrement*sizeof(PhongDrawUniform), + sizeof(PhongDrawUniform)); + if(data.flags & PhongGL::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 1*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(plane); + + shader.bindMaterialBuffer(materialUniform, + 1*data.uniformIncrement*sizeof(PhongMaterialUniform), + sizeof(PhongMaterialUniform)); + shader.bindLightBuffer(lightUniform, + 0*data.uniformIncrement*sizeof(PhongLightUniform), + 2*sizeof(PhongLightUniform)); + shader.bindTransformationBuffer(transformationUniform, + 2*data.uniformIncrement*sizeof(TransformationUniform3D), + sizeof(TransformationUniform3D)); + shader.bindDrawBuffer(drawUniform, + 2*data.uniformIncrement*sizeof(PhongDrawUniform), + sizeof(PhongDrawUniform)); + if(data.flags & PhongGL::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 2*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(cone); + + /* Otherwise using the draw offset */ + } else { + shader.bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .bindLightBuffer(lightUniform); + if(data.flags & PhongGL::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } + + /* + Colored case: + + - Sphere should be lower left, yellow with a white light with red and + green highlight on bottom left and right part + - Plane lower right, cyan with a magenta light so blue + - Cone up center, yellow with a cyan light so green + + Textured case: + + - Sphere should have bottom left numbers, so light 7881, rotated (78 + visible) + - Plane bottom right, 1223 + - Cone 6778 + */ + MAGNUM_VERIFY_NO_GL_ERROR(); + 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, "PhongTestFiles", data.expected}), + (DebugTools::CompareImageToFile{_manager, data.maxThreshold, data.meanThreshold})); + + /* Object ID -- no need to verify the whole image, just check that pixels + on known places have expected values. SwiftShader insists that the read + format has to be 32bit, so the renderbuffer format is that too to make + it the same (ES3 Mesa complains if these don't match). */ + #ifndef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + #endif + { + _framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1}); + CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete); + Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI}); + MAGNUM_VERIFY_NO_GL_ERROR(); + CORRADE_COMPARE(image.pixels()[5][5], 27); /* Outside */ + CORRADE_COMPARE(image.pixels()[24][24], 1211); /* Sphere */ + CORRADE_COMPARE(image.pixels()[24][56], 5627); /* Plane */ + CORRADE_COMPARE(image.pixels()[56][40], 36363); /* Circle */ + } +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::PhongGLTest) diff --git a/src/Magnum/Shaders/Test/PhongTest.cpp b/src/Magnum/Shaders/Test/PhongTest.cpp new file mode 100644 index 000000000..7276b6440 --- /dev/null +++ b/src/Magnum/Shaders/Test/PhongTest.cpp @@ -0,0 +1,322 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include + +#include "Magnum/Math/Matrix4.h" +#include "Magnum/Shaders/Phong.h" + +namespace Magnum { namespace Shaders { namespace Test { namespace { + +struct PhongTest: TestSuite::Tester { + explicit PhongTest(); + + template void uniformSize(); + + void drawUniformConstructDefault(); + void drawUniformConstructNoInit(); + void drawUniformSetters(); + void drawUniformMaterialIdPacking(); + + void materialUniformConstructDefault(); + void materialUniformConstructNoInit(); + void materialUniformSetters(); + + void lightUniformConstructDefault(); + void lightUniformConstructNoInit(); + void lightUniformSetters(); +}; + +PhongTest::PhongTest() { + addTests({&PhongTest::uniformSize, + &PhongTest::uniformSize, + &PhongTest::uniformSize, + + &PhongTest::drawUniformConstructDefault, + &PhongTest::drawUniformConstructNoInit, + &PhongTest::drawUniformSetters, + &PhongTest::drawUniformMaterialIdPacking, + + &PhongTest::materialUniformConstructDefault, + &PhongTest::materialUniformConstructNoInit, + &PhongTest::materialUniformSetters, + + &PhongTest::lightUniformConstructDefault, + &PhongTest::lightUniformConstructNoInit, + &PhongTest::lightUniformSetters}); +} + +using namespace Math::Literals; + +template struct UniformTraits; +template<> struct UniformTraits { + static const char* name() { return "PhongDrawUniform"; } +}; +template<> struct UniformTraits { + static const char* name() { return "PhongMaterialUniform"; } +}; +template<> struct UniformTraits { + static const char* name() { return "PhongLightUniform"; } +}; + +template void PhongTest::uniformSize() { + setTestCaseTemplateName(UniformTraits::name()); + + CORRADE_FAIL_IF(sizeof(T) % sizeof(Vector4) != 0, sizeof(T) << "is not a multiple of vec4 for UBO alignment."); + + /* 48-byte structures are fine, we'll align them to 768 bytes and not + 256, but warn about that */ + CORRADE_FAIL_IF(768 % sizeof(T) != 0, sizeof(T) << "can't fit exactly into 768-byte UBO alignment."); + if(256 % sizeof(T) != 0) + CORRADE_WARN(sizeof(T) << "can't fit exactly into 256-byte UBO alignment, only 768."); +} + +void PhongTest::drawUniformConstructDefault() { + PhongDrawUniform a; + PhongDrawUniform b{DefaultInit}; + CORRADE_COMPARE(a.normalMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); + CORRADE_COMPARE(b.normalMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); + CORRADE_COMPARE(a.materialId, 0); + CORRADE_COMPARE(b.materialId, 0); + CORRADE_COMPARE(a.objectId, 0); + CORRADE_COMPARE(b.objectId, 0); + CORRADE_COMPARE(a.lightOffset, 0); + CORRADE_COMPARE(b.lightOffset, 0); + CORRADE_COMPARE(a.lightCount, 0xffffffffu); + CORRADE_COMPARE(b.lightCount, 0xffffffffu); + + constexpr PhongDrawUniform ca; + constexpr PhongDrawUniform cb{DefaultInit}; + CORRADE_COMPARE(ca.normalMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); + CORRADE_COMPARE(cb.normalMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 1.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f} + })); + CORRADE_COMPARE(ca.materialId, 0); + CORRADE_COMPARE(cb.materialId, 0); + CORRADE_COMPARE(ca.objectId, 0); + CORRADE_COMPARE(cb.objectId, 0); + CORRADE_COMPARE(ca.lightOffset, 0); + CORRADE_COMPARE(cb.lightOffset, 0); + CORRADE_COMPARE(ca.lightCount, 0xffffffffu); + CORRADE_COMPARE(cb.lightCount, 0xffffffffu); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void PhongTest::drawUniformConstructNoInit() { + /* Testing only some fields, should be enough */ + PhongDrawUniform a; + a.normalMatrix[2] = {1.5f, 0.3f, 3.1f, 0.5f}; + a.materialId = 5; + a.lightCount = 7; + + new(&a) PhongDrawUniform{NoInit}; + CORRADE_COMPARE(a.normalMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); + CORRADE_COMPARE(a.materialId, 5); + CORRADE_COMPARE(a.lightCount, 7); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void PhongTest::drawUniformSetters() { + PhongDrawUniform a; + a.setNormalMatrix(Matrix4::rotationX(90.0_degf).normalMatrix()) + .setMaterialId(5) + .setObjectId(7) + .setLightOffsetCount(9, 13); + CORRADE_COMPARE(a.normalMatrix, (Matrix3x4{ + Vector4{1.0f, 0.0f, 0.0f, 0.0f}, + Vector4{0.0f, 0.0f, 1.0f, 0.0f}, + Vector4{0.0f, -1.0f, 0.0f, 0.0f} + })); + CORRADE_COMPARE(a.materialId, 5); + CORRADE_COMPARE(a.objectId, 7); + CORRADE_COMPARE(a.lightOffset, 9); + CORRADE_COMPARE(a.lightCount, 13); +} + +void PhongTest::drawUniformMaterialIdPacking() { + PhongDrawUniform a; + a.setMaterialId(13765); + /* The normalMatrix field is 3x4 floats, materialId should be right after + in the low 16 bits on both LE and BE */ + CORRADE_COMPARE(reinterpret_cast(&a)[12] & 0xffff, 13765); +} + +void PhongTest::materialUniformConstructDefault() { + PhongMaterialUniform a; + PhongMaterialUniform b{DefaultInit}; + CORRADE_COMPARE(a.ambientColor, 0x00000000_rgbaf); + CORRADE_COMPARE(b.ambientColor, 0x00000000_rgbaf); + CORRADE_COMPARE(a.diffuseColor, 0xffffffff_rgbaf); + CORRADE_COMPARE(b.diffuseColor, 0xffffffff_rgbaf); + CORRADE_COMPARE(a.specularColor, 0xffffff00_rgbaf); + CORRADE_COMPARE(b.specularColor, 0xffffff00_rgbaf); + CORRADE_COMPARE(a.normalTextureScale, 1.0f); + CORRADE_COMPARE(b.normalTextureScale, 1.0f); + CORRADE_COMPARE(a.shininess, 80.0f); + CORRADE_COMPARE(b.shininess, 80.0f); + CORRADE_COMPARE(a.alphaMask, 0.5f); + CORRADE_COMPARE(b.alphaMask, 0.5f); + + constexpr PhongMaterialUniform ca; + constexpr PhongMaterialUniform cb{DefaultInit}; + CORRADE_COMPARE(ca.ambientColor, 0x00000000_rgbaf); + CORRADE_COMPARE(cb.ambientColor, 0x00000000_rgbaf); + CORRADE_COMPARE(ca.diffuseColor, 0xffffffff_rgbaf); + CORRADE_COMPARE(cb.diffuseColor, 0xffffffff_rgbaf); + CORRADE_COMPARE(ca.specularColor, 0xffffff00_rgbaf); + CORRADE_COMPARE(cb.specularColor, 0xffffff00_rgbaf); + CORRADE_COMPARE(ca.normalTextureScale, 1.0f); + CORRADE_COMPARE(cb.normalTextureScale, 1.0f); + CORRADE_COMPARE(ca.shininess, 80.0f); + CORRADE_COMPARE(cb.shininess, 80.0f); + CORRADE_COMPARE(ca.alphaMask, 0.5f); + CORRADE_COMPARE(cb.alphaMask, 0.5f); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void PhongTest::materialUniformConstructNoInit() { + /* Testing only some fields, should be enough */ + PhongMaterialUniform a; + a.diffuseColor = 0x354565fc_rgbaf; + a.normalTextureScale = 0.4f; + a.alphaMask = 7.0f; + + new(&a) PhongMaterialUniform{NoInit}; + CORRADE_COMPARE(a.diffuseColor, 0x354565fc_rgbaf); + CORRADE_COMPARE(a.normalTextureScale, 0.4f); + CORRADE_COMPARE(a.alphaMask, 7.0f); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void PhongTest::materialUniformSetters() { + PhongMaterialUniform a; + a.setAmbientColor(0xff3366cc_rgbaf) + .setDiffuseColor(0x996600aa_rgbaf) + .setSpecularColor(0x0044ffdd_rgbaf) + .setNormalTextureScale(0.4f) + .setShininess(37.0f) + .setAlphaMask(2.5f); + CORRADE_COMPARE(a.ambientColor, 0xff3366cc_rgbaf); + CORRADE_COMPARE(a.diffuseColor, 0x996600aa_rgbaf); + CORRADE_COMPARE(a.specularColor, 0x0044ffdd_rgbaf); + CORRADE_COMPARE(a.normalTextureScale, 0.4f); + CORRADE_COMPARE(a.shininess, 37.0f); + CORRADE_COMPARE(a.alphaMask, 2.5f); +} + +void PhongTest::lightUniformConstructDefault() { + PhongLightUniform a; + PhongLightUniform b{DefaultInit}; + CORRADE_COMPARE(a.position, (Vector4{0.0f, 0.0f, 1.0f, 0.0f})); + CORRADE_COMPARE(b.position, (Vector4{0.0f, 0.0f, 1.0f, 0.0f})); + CORRADE_COMPARE(a.color, 0xffffff_rgbf); + CORRADE_COMPARE(b.color, 0xffffff_rgbf); + CORRADE_COMPARE(a.specularColor, 0xffffff_rgbf); + CORRADE_COMPARE(b.specularColor, 0xffffff_rgbf); + CORRADE_COMPARE(a.range, Constants::inf()); + CORRADE_COMPARE(b.range, Constants::inf()); + + constexpr PhongLightUniform ca; + constexpr PhongLightUniform cb{DefaultInit}; + CORRADE_COMPARE(ca.position, (Vector4{0.0f, 0.0f, 1.0f, 0.0f})); + CORRADE_COMPARE(cb.position, (Vector4{0.0f, 0.0f, 1.0f, 0.0f})); + CORRADE_COMPARE(ca.color, 0xffffff_rgbf); + CORRADE_COMPARE(cb.color, 0xffffff_rgbf); + CORRADE_COMPARE(ca.specularColor, 0xffffff_rgbf); + CORRADE_COMPARE(cb.specularColor, 0xffffff_rgbf); + CORRADE_COMPARE(ca.range, Constants::inf()); + CORRADE_COMPARE(cb.range, Constants::inf()); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void PhongTest::lightUniformConstructNoInit() { + /* Testing only some fields, should be enough */ + PhongLightUniform a; + a.color = 0x354565_rgbf; + a.range = 7.0f; + + new(&a) PhongLightUniform{NoInit}; + CORRADE_COMPARE(a.color, 0x354565_rgbf); + CORRADE_COMPARE(a.range, 7.0f); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void PhongTest::lightUniformSetters() { + PhongLightUniform a; + a.setPosition({2.5f, 3.6f, 0.7f, 1.1f}) + .setColor(0x354565_rgbf) + .setSpecularColor(0x996600_rgbf) + .setRange(7.0f); + CORRADE_COMPARE(a.position, (Vector4{2.5f, 3.6f, 0.7f, 1.1f})); + CORRADE_COMPARE(a.color, 0x354565_rgbf); + CORRADE_COMPARE(a.specularColor, 0x996600_rgbf); + CORRADE_COMPARE(a.range, 7.0f); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Shaders::Test::PhongTest) diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/multidraw-textured.tga b/src/Magnum/Shaders/Test/PhongTestFiles/multidraw-textured.tga new file mode 100644 index 0000000000000000000000000000000000000000..3c7ce9617431f66956f2e493b9f99d0ab09c4563 GIT binary patch literal 5524 zcma)<2~bp5*2fVP*@bR;rMu~cUZJ7cnr2^Q-w_uUH$+85L2!d8VzbF60-~shyMW+^ z7!#6N%r~h?;*6S@L=%&km`pO6FSF0oEHz)v`~LU62eu|tHPk)z9^Lnz^Sl3Z?s>26 z>|E?-|MKnf?Syvsy}Z2s8ymnpr`10-ufAr6H|CiVM?p!_e)@0BBjCEpa53z~|(LF); z-|E7BFU)c2q-puJ;1yFL@Kb~_9fJ8**utMrbiaA23rlZbJn-XlZ8N8vuMh3T60)>q z)B76TYc zSzjJ*nmX2aW$>lZ-kn%U;M(D_&Bh~v_0AkJWDf`AoC?T2W6V1noOdoH?_6N)*_Ahk zo5(oPG<|#@9D(cVkv)@#8^-&0U+mp+rn7daxeDt$V6E`mTs9MBzwvG~ZV|x>YlQ?3 zrZ;~S^70ka8?WJ?7FLsPEa4}2Y18_2>4)_hM*=d3ax;%vvySIv9S@v5Tt7KbKQT~$ zsed;@jrHvs?b~^wZ~M8Py3-xohTEzUw!d*>=dLn%IY5F}eh_P4a?(<9H-=!qNN^Hs zLD0RQhg=`WUFg`EpkMq9H_a>p(N_?)Lz&Q`UfiKc?$)OCWTo^{sfV7X_T{AY8P0Uo zlF?avx^vs9j+&8`9B=YXDemqpExNI*hK} zWnH_-kR>r!%Okd_B5IYEIyo8J z<@>7i6sNkz*^d90T>3L7_mS_$n0pc{%&%y)NgwM&?R(})VN$6xLRz;6a|+{ zLN-W4Dx~<3j7lluTVd|o%M0q(<=2+w*R07657btz%HC9(S+z2&dUejar70M7>DoZjkHkD*l6lYWvrEge~R=zCN-_LhV!Qz#9iN)FR2@%Hh*ief} zr&q}UzqvFEPFrD%c0JA9`9;Q#&(pU*N&7)gMobVsgCxa2M+OeLiKkBI`=~jZ9KJT6 zuPYGfmI(BPWUR_hT9uy&2#nI)gyNj|qO3ST#Dw}shZuqls*L!s^tiC1?6|`8Xy~}H zD4iwq-1ALd!>@AczshD1!3Q-N9g%NFM8JhSxHAW5g^JI?`L7Tvqj^#z*EgE0Sj<(X zaFwavsx)s^y0>4tcR@-dMt+JVFWHj2I3g#>oDmlqW>hCd24%(*rjnd^NVs-MGUQyp zJguxSnT7M(|J~B2|1E6%wt%7B{bd$ZWF(x+GcpXg7x~(3#e-C;B%UIHD^KLeV!R}g zp3-P9X^fXFmW;?~ZE}=pF-Bxik|hvHOtb{1!~_ElxCN<^%Q9jJWo`o0h0;j$EMNW~ zzs>#qH(B3(mGPUWsoy+V{M9FkPd|=(@-XJp527AFuzd8c`Qg3r5ATLs-V2L(7bDbs zKP3EKGpsc@?CxxG-K7SuZzO>OlTXAVQHX~i$X#gi5C)TBFi9eUv=KoXbD+jO+^EJS zDnuV|4ghajY)EDTjhPZzm=+CE3`oraa*F@?r{#b9ec@le18Meee+A5xuRlxr@^Sp< zk8BwuqTXj10mv{~Utsh>Ql%Cz=mtPQ@dMnrT33#r3rCINDp7L{ep0Af32ndvGV`^TJ(=UF8%9o^ZxK_ z=$ZEG&mm^Q7azrb_93IBCHez%O|)>4 z$XO!f`UtpE5l<-%;f+73qMhA-K>L1z-u* zp$0I3&T@J;QdB)f)lt+IirPs1dkyvX)wgGm`kdc=&7d+#kB)o9pfXxoB94ce-vJ@X zcr%Mg-Y|sTdN(B3Q||2UH?d_cwE&wHGk-LEF2~k0*l8GRy z!B1w?%7gTZV1p94a=Cm~#~zAmr>G{HavMc$0_EFppg@6$(nQ8lmpD7I2_HwrKD14D zWJFh}`3~@&*O5Rvc`BV3$(-E93tf1QPM-4@xH>pE+S|<|@3U5z+k6KHXD3GwHy4g4 zAr%WaGBHmf5dc;#7h|YoLii!?0K5^3I!aN!6xBvidub(W-n)aOgR$UGzd=>v?BIOI zCw&qV|BQq(9#0cn7uIw*=H_woDC3~62Q_dnp2;4G7Vll<8a3D)}p z40-o;AmWhp5sVJlLZs1g!F=fB>f+?);tU>q$^wXQQ`9Yrx=c}LC~Al16Rh7 zHd?hcE%7i%eI+6a>|R296ECoFlL??(`F&wr1i-lC}2 zDGGZ<5Ns5}ql8hvP#x_6O^bA_^rs1nKV^2woG_-7K5JC-o&_L3I}RcM{f_)4MLnXZ zJ9G?e1Y#n&C_tPM9B1qTrAL}g=2uB6PvR0EV;8n{_k#iY5uh6ZNP78yrl_y!7}y8I zLc&l$C~|B9Edy;0%@+@djO?$Um+4uzW%FdSgF!eZMM8c!@hOTz4D171At4Y7o4`Rr zIiM|}b)q(*QfAIKj7r}x%QJc%)Y=mo{TZ#{qTYW( zA8^HByzXzD_78m7Kk!yS;48TagW<0oF}yluc=fve);0ajE4mw#x@+Uw$%~rNGwQR$ zs^KGw!v}pkTP2P4qOFyjf?UKn3Ofdq2}J88vR!gzo6_%)pJqs-JEb*T(D`4Q4Rpgc zQ0UA_66lrLKvNUit7F=U^XiMI{7xTN9vhG!>XNnX_t{m;FIx@oWH)paZ{8BIq(&xh zQmDFBnnAU0M5{liGmPm2E*k=_`5O`C=G=BnGv}X0nZB&MKB2ubsu@40K0o3&GNc?l zB=2sQ?rRWLZ|3CZuu+cVDiMmxrLuat5~B61wZj_yS)E~&31P4ak&|%5ICJUw5SO%< zN1w^n-|c&#*=Khhzl{7IAv>V`d-Do?q_w^ZtJ1HR){05Wq#@v{!8kRy1^D-$xz75x zNe8Ra(LVXX1Jb6KL^Uiw@qYp)7K%4WefKaD%_V|$PBKo?ofCi5FmvOX^Qj5l)v-Bo z6az?fi%QuIO?iuCn56z#!d~QD&R+-VRyIK4IksmWcMl6O^ES zlw^-Yw7If;&d-g@q&Yw?bcPdyXba-HYECZsVMKOz3dTXHuv?>B0f8}2(@u^tDiO`E zjth9(U7i_708mJziXa8Eg?6j3s)CJ7ua){N_m%G_seuSal4XR>j62S*y@m?Bh<=4* zDBiJ+lTP+dsL#MW>T@KGh32B9y71tWHVa9|gKp}f5x?qV^6++%PZeg9X!NSpG5p?A z#o0~ju`10}o&MHN!)RRuY85>J{Ripr(;oL#G;_G5CdsO^Ft*)|LBg=R$pxwz8t&D#%{~XL^UxD;k znQDBC_QvdFoFl-X*l=uFuk_5aIR4-odbgF02QIdlCAiTc%tX5ix&))si4E`oC)+~E;o*ya{*UJguApwOHa6oj^wLoVOP`N zu1^pEhW%E1g2T;VzrBd-d^jK9j_5b>*uwv01^qep3t5096q9NspO3=qv3*Fy&Z0lD S7Rp3oQZr z2%@V4Dy|AHu7JAY%36lns{OWKdw=)e|Mz@Du(Z0%ejV;T_ng!H-XLWv_TLDNP*a`E z&(HrOeJT5r^z+a9_17ppRloeApMKJhKkEE>ma6HC=&3q)PTzf}@4wd%Kj^}RD7~Hy znK*x5-+pWBr%$tUK7F;5UgiG_gOjID>B}#5=8VpsWmgeBVnZgr`pQ8JYaGo3tXji@vRoMn=wPd?GdAKT9N-`6|u=o^T8qK@yGM28hz=eknD*|~ zmMt8gYhekwC=6M_=K^itu6_GdU9J5!s`*9-oeq7Y!$)=au(d;nEC{XUfNJ*Zz*~Cr z4ei;Z9Xp7r_3O1_1yRcBB`!I+l;{h?=tf5qEnB9I8*S_M9oqG|c028H+Iw32-mt7R zz!7fuZtdQsBRlk;Z92C_+p4r-gVwCk^5sMb6BN0Wl;SYS{DdSCEnH|D*}kq)8~&$C zr>g&I(@t&L6b*V+RhB9%wQ+;0*UP;ww_0DX)biJ~Xpt5y(Bj23l9bVlTxzN%Dk4z{ z)l?)TN>@pVUVYV0Em@&u`?dVAR;<&?mBsWNnDB75VyQk{EVodeAApFCWtv-|c@-9^qN13dW5ap#R9>#K zGL@HTSyapBsBo6%%u!L1N=yAxJY^)3>C)55Y%MK~)Yei`tR*K~OG+{a&#eQ3U^8dx z`RDcGi+cHG&6;JHm@F(TqUYL>iC12+71r51V}@RMUNffarI&0T{Lv^a#F6E)vRr1S zwTuiGiG+|6@gyflL1R$L>p&s)bI<9CC#*gFw5CqAkJF|Bu9%){LnfGJ@yRFk_~W*b zkAMdgXn;tYJ{>3}^gNfB=W=shPL9jYzC571>=Xg1tPUiPXW~SS9jkHUG=97oPnx93 zlZ)t4hB-FFJge+^^ilgbWs079%8-G0`gCj0J{w}H?;1674H~%o{16<-Ak0%0*R3wB z=tZzHtZvdg@`y%`)P48q!3XuwLmD;8+UU_ydY%ngWSYhM@7Dtl*bFBgd(7~I5C-A# znP(V;*z#SoX0B;dS5OdwtY6>g^W4Riyu?!mue1(p6DEY%?!8w-hUm^aHF&TM?zyL! zo@B$pgR^zlT{b^-s78!1pku~__$Y1iWNYKc$C0&d>sq&VEn2wd&0UiwAwJ|qTTh>N zej>P}B-67b0a=D6Ad`nRXps8%SMT2H+t*sZe$k-!?VDqhefsFO+jPer26osmW0`}a zN9*B-EeKMN8Wnb?c^UuT|Hsv}9d>y&vW3y6bFo(a$_=-}G4F*Z>fKVn97j`Qg6YQn2wIEzCb?ctA@Zlr{@n{Lt#H>gV&U46A$ zwp6QDQF@jQ+q6-K4u+leo;{6H*ya$F25v~k3Lqw&y_gN%fB~*wKi8|5>(RqdcJA!j zwKF#Hq=)FK#D|I3BzVt*=mRnr6ZYDdLw`vta^lJ0&d5GSM ziBiWP+Bb7k_2^*=lDlnNU3HZjH&$MrA`vAgTVnvqMvc_08HE0Zl6ty#H{Br~xE(sg z@r@npMvZbKMi|P016}XlMx=)biwKP&xiH*j9vf~;qRL50CUGLZh-r#`ty>#{h7FaI zqx5toCGm4EO3zkmsxmVTvj^!FZ{J=WI~wDh?${A*_O094O`YndOmXAJxsfB?uwh1G zpFSZxM8=2Q7am1#!Yk1i4HdTEN@+$J0_Nu{D@$o&VSMf;dhy12LayK^6$B_vovf(jCtyaled)*U!tIj94& z5DWw1Z;IfR;34C|4XIKOVdnZe>W7kQ>QEkb-)#ZKomuAhp+=L5sxKT z2bTQn153P)7a8-C1c`swtRzVrO_Iu`wbD*$zx1B;k@T5#S~??rBYh|ROZq{&ApJXN z4EXqcu*3$ov6&+u2FD4qLs}!1iQwe0-{-67t@LN4b{`yWX3Pw8vv zE9ndAZ_+X8i1dcES;A2-Sr)6tXZaIn&-kv^1>3D)8|i zm+*3Bj4Lk+*gy!*^Oux>Mkp1R!?Q&BkgzVe@&SH}zlPpiv!zwR>cIpdzy`uA1@{to zawz$2X?q~(Ok>7S`^Zm9i-RRL#L2&GWK;lloc6ed_DP#~C!`6n$ZLt785$McQQs3D zw`d?}jv3Uulekm@S#a&SI1pHTnN(;&4XO|NbWp=?2_kSXAbu|>1XBur#~^Tkk+E|{ zh}433)8 zJt)c(#D08SCxoZQmqYyvrN?3+HV~%1Jx0o7r2>z6}7z28bomCR?@Besy+mvHU9WgR{13|8kXT!Tr(} nJ7p*Q{Q3Yt>4Im)=C{(5t*Fzs({}lp%J5p-Oyc+OmB#-E3-H_I literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/VectorGLTest.cpp b/src/Magnum/Shaders/Test/VectorGLTest.cpp index 581281b6c..0a25f7038 100644 --- a/src/Magnum/Shaders/Test/VectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VectorGLTest.cpp @@ -52,6 +52,21 @@ #include "Magnum/Trade/ImageData.h" #include "Magnum/Trade/MeshData.h" +#ifndef MAGNUM_TARGET_GLES2 +#include "Magnum/GL/Extensions.h" +#include "Magnum/GL/MeshView.h" +#include "Magnum/MeshTools/Concatenate.h" +#include "Magnum/MeshTools/GenerateIndices.h" +#include "Magnum/Primitives/Circle.h" +#include "Magnum/Primitives/Cone.h" +#include "Magnum/Primitives/Plane.h" +#include "Magnum/Primitives/Square.h" +#include "Magnum/Primitives/UVSphere.h" +#include "Magnum/Shaders/Generic.h" +#include "Magnum/Shaders/Generic.h" +#include "Magnum/Shaders/Vector.h" +#endif + #include "configure.h" namespace Magnum { namespace Shaders { namespace Test { namespace { @@ -60,17 +75,43 @@ struct VectorGLTest: GL::OpenGLTester { explicit VectorGLTest(); template void construct(); + #ifndef MAGNUM_TARGET_GLES2 + template void constructUniformBuffers(); + #endif + template void constructMove(); + #ifndef MAGNUM_TARGET_GLES2 + template void constructMoveUniformBuffers(); + #endif + #ifndef MAGNUM_TARGET_GLES2 + template void constructUniformBuffersZeroDraws(); + #endif + + #ifndef MAGNUM_TARGET_GLES2 + template void setUniformUniformBuffersEnabled(); + template void bindBufferUniformBuffersNotEnabled(); + #endif template void setTextureMatrixNotEnabled(); + #ifndef MAGNUM_TARGET_GLES2 + template void bindTextureTransformBufferNotEnabled(); + #endif + #ifndef MAGNUM_TARGET_GLES2 + template void setWrongDrawOffset(); + #endif void renderSetup(); void renderTeardown(); - void renderDefaults2D(); - void renderDefaults3D(); - void render2D(); - void render3D(); + template void renderDefaults2D(); + template void renderDefaults3D(); + template void render2D(); + template void render3D(); + + #ifndef MAGNUM_TARGET_GLES2 + void renderMulti2D(); + void renderMulti3D(); + #endif private: PluginManager::Manager _manager{"nonexistent"}; @@ -105,6 +146,19 @@ constexpr struct { {"texture transformation", VectorGL2D::Flag::TextureTransformation} }; +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + VectorGL2D::Flags flags; + UnsignedInt drawCount; +} ConstructUniformBuffersData[]{ + {"classic fallback", {}, 1}, + {"", VectorGL2D::Flag::UniformBuffers, 1}, + {"texture transformation", VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, 1}, + {"multiple draws", VectorGL2D::Flag::UniformBuffers, 128}, +}; +#endif + const struct { const char* name; VectorGL2D::Flags flags; @@ -122,30 +176,102 @@ const struct { "vector2D.tga", "vector3D.tga", false} }; +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + const char* expected2D; + const char* expected3D; + UnsignedInt drawCount; + UnsignedInt uniformIncrement; + Float maxThreshold, meanThreshold; +} RenderMultiData[] { + {"bind with offset", "multidraw2D.tga", "multidraw3D.tga", + 1, 16, 0.0f, 0.0f}, + {"draw offset", "multidraw2D.tga", "multidraw3D.tga", + 3, 1, 0.0f, 0.0f}, +}; +#endif + VectorGLTest::VectorGLTest() { addInstancedTests({ &VectorGLTest::construct<2>, &VectorGLTest::construct<3>}, Containers::arraySize(ConstructData)); + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({ + &VectorGLTest::constructUniformBuffers<2>, + &VectorGLTest::constructUniformBuffers<3>}, + Containers::arraySize(ConstructUniformBuffersData)); + #endif + addTests({ &VectorGLTest::constructMove<2>, &VectorGLTest::constructMove<3>, + #ifndef MAGNUM_TARGET_GLES2 + &VectorGLTest::constructMoveUniformBuffers<2>, + &VectorGLTest::constructMoveUniformBuffers<3>, + #endif + + #ifndef MAGNUM_TARGET_GLES2 + &VectorGLTest::constructUniformBuffersZeroDraws<2>, + &VectorGLTest::constructUniformBuffersZeroDraws<3>, + #endif + + #ifndef MAGNUM_TARGET_GLES2 + &VectorGLTest::setUniformUniformBuffersEnabled<2>, + &VectorGLTest::setUniformUniformBuffersEnabled<3>, + &VectorGLTest::bindBufferUniformBuffersNotEnabled<2>, + &VectorGLTest::bindBufferUniformBuffersNotEnabled<3>, + #endif &VectorGLTest::setTextureMatrixNotEnabled<2>, - &VectorGLTest::setTextureMatrixNotEnabled<3>}); + &VectorGLTest::setTextureMatrixNotEnabled<3>, + #ifndef MAGNUM_TARGET_GLES2 + &VectorGLTest::bindTextureTransformBufferNotEnabled<2>, + &VectorGLTest::bindTextureTransformBufferNotEnabled<3>, + #endif + #ifndef MAGNUM_TARGET_GLES2 + &VectorGLTest::setWrongDrawOffset<2>, + &VectorGLTest::setWrongDrawOffset<3> + #endif + }); - addTests({&VectorGLTest::renderDefaults2D, - &VectorGLTest::renderDefaults3D}, + addTests({ + &VectorGLTest::renderDefaults2D, + #ifndef MAGNUM_TARGET_GLES2 + &VectorGLTest::renderDefaults2D, + #endif + &VectorGLTest::renderDefaults3D, + #ifndef MAGNUM_TARGET_GLES2 + &VectorGLTest::renderDefaults3D, + #endif + }, &VectorGLTest::renderSetup, &VectorGLTest::renderTeardown); - addInstancedTests({&VectorGLTest::render2D, - &VectorGLTest::render3D}, + addInstancedTests({ + &VectorGLTest::render2D, + #ifndef MAGNUM_TARGET_GLES2 + &VectorGLTest::render2D, + #endif + &VectorGLTest::render3D, + #ifndef MAGNUM_TARGET_GLES2 + &VectorGLTest::render3D, + #endif + }, Containers::arraySize(RenderData), &VectorGLTest::renderSetup, &VectorGLTest::renderTeardown); + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({&VectorGLTest::renderMulti2D, + &VectorGLTest::renderMulti3D}, + Containers::arraySize(RenderMultiData), + &VectorGLTest::renderSetup, + &VectorGLTest::renderTeardown); + #endif + /* 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 @@ -189,6 +315,33 @@ template void VectorGLTest::construct() { MAGNUM_VERIFY_NO_GL_ERROR(); } +#ifndef MAGNUM_TARGET_GLES2 +template void VectorGLTest::constructUniformBuffers() { + setTestCaseTemplateName(std::to_string(dimensions)); + + auto&& data = ConstructUniformBuffersData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if((data.flags & VectorGL::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + VectorGL shader{data.flags, data.drawCount}; + CORRADE_COMPARE(shader.flags(), data.flags); + CORRADE_COMPARE(shader.drawCount(), data.drawCount); + CORRADE_VERIFY(shader.id()); + { + #ifdef CORRADE_TARGET_APPLE + CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); + #endif + CORRADE_VERIFY(shader.validate().first); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); +} +#endif + template void VectorGLTest::constructMove() { setTestCaseTemplateName(std::to_string(dimensions)); @@ -210,6 +363,115 @@ template void VectorGLTest::constructMove() { CORRADE_VERIFY(!b.id()); } +#ifndef MAGNUM_TARGET_GLES2 +template void VectorGLTest::constructMoveUniformBuffers() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + VectorGL a{VectorGL::Flag::UniformBuffers, 5}; + const GLuint id = a.id(); + CORRADE_VERIFY(id); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + VectorGL b{std::move(a)}; + CORRADE_COMPARE(b.id(), id); + CORRADE_COMPARE(b.flags(), VectorGL::Flag::UniformBuffers); + CORRADE_COMPARE(b.drawCount(), 5); + CORRADE_VERIFY(!a.id()); + + VectorGL c{NoCreate}; + c = std::move(b); + CORRADE_COMPARE(c.id(), id); + CORRADE_COMPARE(c.flags(), VectorGL::Flag::UniformBuffers); + CORRADE_COMPARE(c.drawCount(), 5); + CORRADE_VERIFY(!b.id()); +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +template void VectorGLTest::constructUniformBuffersZeroDraws() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + VectorGL{VectorGL::Flag::UniformBuffers, 0}; + CORRADE_COMPARE(out.str(), + "Shaders::VectorGL: draw count can't be zero\n"); +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +template void VectorGLTest::setUniformUniformBuffersEnabled() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + VectorGL shader{VectorGL::Flag::UniformBuffers}; + shader.setTransformationProjectionMatrix({}) + .setTextureMatrix({}) + .setBackgroundColor({}) + .setColor({}); + CORRADE_COMPARE(out.str(), + "Shaders::VectorGL::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::VectorGL::setTextureMatrix(): the shader was created with uniform buffers enabled\n" + "Shaders::VectorGL::setBackgroundColor(): the shader was created with uniform buffers enabled\n" + "Shaders::VectorGL::setColor(): the shader was created with uniform buffers enabled\n"); +} + +template void VectorGLTest::bindBufferUniformBuffersNotEnabled() { + 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}; + + GL::Buffer buffer; + VectorGL shader; + shader.bindTransformationProjectionBuffer(buffer) + .bindTransformationProjectionBuffer(buffer, 0, 16) + .bindDrawBuffer(buffer) + .bindDrawBuffer(buffer, 0, 16) + .bindTextureTransformationBuffer(buffer) + .bindTextureTransformationBuffer(buffer, 0, 16) + .setDrawOffset(0); + CORRADE_COMPARE(out.str(), + "Shaders::VectorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::VectorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::VectorGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::VectorGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::VectorGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::VectorGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::VectorGL::setDrawOffset(): the shader was not created with uniform buffers enabled\n"); +} +#endif + template void VectorGLTest::setTextureMatrixNotEnabled() { setTestCaseTemplateName(std::to_string(dimensions)); @@ -227,6 +489,54 @@ template void VectorGLTest::setTextureMatrixNotEnabled() "Shaders::VectorGL::setTextureMatrix(): the shader was not created with texture transformation enabled\n"); } +#ifndef MAGNUM_TARGET_GLES2 +template void VectorGLTest::bindTextureTransformBufferNotEnabled() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + GL::Buffer buffer{GL::Buffer::TargetHint::Uniform}; + VectorGL shader{VectorGL::Flag::UniformBuffers}; + shader.bindTextureTransformationBuffer(buffer) + .bindTextureTransformationBuffer(buffer, 0, 16); + CORRADE_COMPARE(out.str(), + "Shaders::VectorGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled\n" + "Shaders::VectorGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled\n"); +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +template void VectorGLTest::setWrongDrawOffset() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + VectorGL{VectorGL::Flag::UniformBuffers, 5} + .setDrawOffset(5); + CORRADE_COMPARE(out.str(), + "Shaders::VectorGL::setDrawOffset(): draw offset 5 is out of bounds for 5 draws\n"); +} +#endif + constexpr Vector2i RenderSize{80, 80}; void VectorGLTest::renderSetup() { @@ -262,7 +572,18 @@ constexpr GL::TextureFormat TextureFormatR = #endif ; -void VectorGLTest::renderDefaults2D() { +template void VectorGLTest::renderDefaults2D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == VectorGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -288,9 +609,26 @@ void VectorGLTest::renderDefaults2D() { .setSubImage(0, {}, *image); #endif - VectorGL2D{} - .bindVectorTexture(texture) - .draw(square); + VectorGL2D shader{flag}; + shader.bindVectorTexture(texture); + + if(flag == VectorGL2D::Flag{}) { + shader.draw(square); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == VectorGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + VectorDrawUniform{} + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(square); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -309,7 +647,18 @@ void VectorGLTest::renderDefaults2D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void VectorGLTest::renderDefaults3D() { +template void VectorGLTest::renderDefaults3D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == VectorGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -335,9 +684,26 @@ void VectorGLTest::renderDefaults3D() { .setSubImage(0, {}, *image); #endif - VectorGL3D{} - .bindVectorTexture(texture) - .draw(plane); + VectorGL3D shader{flag}; + shader.bindVectorTexture(texture); + + if(flag == VectorGL3D::Flag{}) { + shader.draw(plane); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == VectorGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform3D{} + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + VectorDrawUniform{} + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(plane); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -356,10 +722,21 @@ void VectorGLTest::renderDefaults3D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void VectorGLTest::render2D() { +template void VectorGLTest::render2D() { auto&& data = RenderData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == VectorGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -385,18 +762,46 @@ void VectorGLTest::render2D() { .setSubImage(0, {}, *image); #endif - VectorGL2D shader{data.flags}; - shader.setBackgroundColor(data.backgroundColor) - .setColor(data.color) - .bindVectorTexture(texture); - - if(data.textureTransformation != Matrix3{}) - shader.setTextureMatrix(data.textureTransformation); - else shader.setTransformationProjectionMatrix( - Matrix3::projection({2.1f, 2.1f})* - Matrix3::rotation(5.0_degf)); - - shader.draw(square); + VectorGL2D shader{data.flags|flag}; + shader.bindVectorTexture(texture); + + if(flag == VectorGL2D::Flag{}) { + shader.setBackgroundColor(data.backgroundColor) + .setColor(data.color); + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); + else shader.setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::rotation(5.0_degf)); + shader.draw(square); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == VectorGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + data.textureTransformation == Matrix3{} ? + Matrix3::projection({2.1f, 2.1f})* + Matrix3::rotation(5.0_degf) : Matrix3{} + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + VectorDrawUniform{} + .setBackgroundColor(data.backgroundColor) + .setColor(data.color) + }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setTextureMatrix(data.textureTransformation) + }}; + if(data.flags & VectorGL2D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(square); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -418,10 +823,21 @@ void VectorGLTest::render2D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -void VectorGLTest::render3D() { +template void VectorGLTest::render3D() { auto&& data = RenderData[testCaseInstanceId()]; setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 + if(flag == VectorGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName("Flag::UniformBuffers"); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -447,20 +863,50 @@ void VectorGLTest::render3D() { .setSubImage(0, {}, *image); #endif - VectorGL3D shader{data.flags}; - shader.setBackgroundColor(data.backgroundColor) - .setColor(data.color) - .bindVectorTexture(texture); - - if(data.textureTransformation != Matrix3{}) - shader.setTextureMatrix(data.textureTransformation); - else shader.setTransformationProjectionMatrix( - Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* - Matrix4::translation(Vector3::zAxis(-2.15f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationZ(15.0_degf)); - - shader.draw(plane); + VectorGL3D shader{data.flags|flag}; + shader.bindVectorTexture(texture); + + if(flag == VectorGL3D::Flag{}) { + shader.setBackgroundColor(data.backgroundColor) + .setColor(data.color); + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); + else shader.setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationZ(15.0_degf)); + shader.draw(plane); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == VectorGL3D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + data.textureTransformation == Matrix3{} ? + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationZ(15.0_degf) : Matrix4{} + ) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + VectorDrawUniform{} + .setBackgroundColor(data.backgroundColor) + .setColor(data.color) + }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setTextureMatrix(data.textureTransformation) + }}; + if(data.flags & VectorGL3D::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .draw(plane); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -482,6 +928,333 @@ void VectorGLTest::render3D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } +#ifndef MAGNUM_TARGET_GLES2 +void VectorGLTest::renderMulti2D() { + auto&& data = RenderMultiData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::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."); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + Containers::Optional image; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/vector.tga")) && (image = importer->image2D(0))); + GL::Texture2D vector; + vector.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R8, image->size()) + .setSubImage(0, {}, *image); + + /* Circle is a fan, plane is a strip, make it indexed first */ + Trade::MeshData circleData = MeshTools::generateIndices(Primitives::circle2DSolid(32, + Primitives::Circle2DFlag::TextureCoordinates)); + Trade::MeshData squareData = MeshTools::generateIndices(Primitives::squareSolid( + Primitives::SquareFlag::TextureCoordinates)); + Trade::MeshData triangleData = MeshTools::generateIndices(Primitives::circle2DSolid(3, + Primitives::Circle2DFlag::TextureCoordinates)); + GL::Mesh mesh = MeshTools::compile(MeshTools::concatenate({circleData, squareData, triangleData})); + GL::MeshView circle{mesh}; + circle.setCount(circleData.indexCount()); + GL::MeshView square{mesh}; + square.setCount(squareData.indexCount()) + .setIndexRange(circleData.indexCount()); + GL::MeshView triangle{mesh}; + triangle.setCount(triangleData.indexCount()) + .setIndexRange(circleData.indexCount() + squareData.indexCount()); + + /* Some drivers have uniform offset alignment as high as 256, which means + the subsequent sets of uniforms have to be aligned to a multiply of it. + The data.uniformIncrement is set high enough to ensure that, in the + non-offset-bind case this value is 1. */ + + Containers::Array transformationProjectionData{2*data.uniformIncrement + 1}; + transformationProjectionData[0*data.uniformIncrement] = TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})* + Matrix3::translation({-1.25f, -1.25f}) + ); + transformationProjectionData[1*data.uniformIncrement] = TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})* + Matrix3::translation({ 1.25f, -1.25f}) + ); + transformationProjectionData[2*data.uniformIncrement] = TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})* + Matrix3::translation({ 0.00f, 1.25f}) + ); + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, transformationProjectionData}; + + Containers::Array textureTransformationData{2*data.uniformIncrement + 1}; + textureTransformationData[0*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::translation({0.5f, 0.5f})* + Matrix3::rotation(180.0_degf)* + Matrix3::translation({-0.5f, -0.5f}) + ); + textureTransformationData[1*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::translation(Vector2::xAxis(1.0f))* + Matrix3::scaling(Vector2::xScale(-1.0f)) + ); + textureTransformationData[2*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix(Matrix3{}); + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; + + Containers::Array drawData{2*data.uniformIncrement + 1}; + drawData[0*data.uniformIncrement] = VectorDrawUniform{} + .setColor(0x00ff00_rgbf) + .setBackgroundColor(0xccffcc_rgbf); + drawData[1*data.uniformIncrement] = VectorDrawUniform{} + .setColor(0xff0000_rgbf) + .setBackgroundColor(0xffcccc_rgbf); + drawData[2*data.uniformIncrement] = VectorDrawUniform{} + .setColor(0x00ff00_rgbf) + .setBackgroundColor(0xccffcc_rgbf); + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; + + VectorGL2D shader{VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, data.drawCount}; + shader.bindVectorTexture(vector); + + /* Just one draw, rebinding UBOs each time */ + if(data.drawCount == 1) { + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 0*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), + sizeof(TransformationProjectionUniform2D)); + shader.bindDrawBuffer(drawUniform, + 0*data.uniformIncrement*sizeof(VectorDrawUniform), + sizeof(VectorDrawUniform)); + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 0*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(circle); + + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 1*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), + sizeof(TransformationProjectionUniform2D)); + shader.bindDrawBuffer(drawUniform, + 1*data.uniformIncrement*sizeof(VectorDrawUniform), + sizeof(VectorDrawUniform)); + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 1*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(square); + + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 2*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), + sizeof(TransformationProjectionUniform2D)); + shader.bindDrawBuffer(drawUniform, + 2*data.uniformIncrement*sizeof(VectorDrawUniform), + sizeof(VectorDrawUniform)); + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 2*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(triangle); + + /* Otherwise using the draw offset */ + } else { + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindTextureTransformationBuffer(textureTransformationUniform); + shader.setDrawOffset(0) + .draw(circle); + shader.setDrawOffset(1) + .draw(square); + shader.setDrawOffset(2) + .draw(triangle); + } + + /* + - Circle lower left, green, upside down + - Square lower right, red, mirrored + - Triangle up center, green + */ + MAGNUM_VERIFY_NO_GL_ERROR(); + 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, "VectorTestFiles", data.expected2D}), + (DebugTools::CompareImageToFile{_manager, data.maxThreshold, data.meanThreshold})); +} + +void VectorGLTest::renderMulti3D() { + auto&& data = RenderMultiData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::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."); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + Containers::Optional image; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/vector.tga")) && (image = importer->image2D(0))); + GL::Texture2D vector; + vector.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R8, image->size()) + .setSubImage(0, {}, *image); + + Trade::MeshData sphereData = Primitives::uvSphereSolid(16, 32, + Primitives::UVSphereFlag::TextureCoordinates); + /* Plane is a strip, make it indexed first */ + Trade::MeshData planeData = MeshTools::generateIndices(Primitives::planeSolid( + Primitives::PlaneFlag::TextureCoordinates)); + Trade::MeshData coneData = Primitives::coneSolid(1, 32, 1.0f, + Primitives::ConeFlag::TextureCoordinates); + GL::Mesh mesh = MeshTools::compile(MeshTools::concatenate({sphereData, planeData, coneData})); + GL::MeshView sphere{mesh}; + sphere.setCount(sphereData.indexCount()); + GL::MeshView plane{mesh}; + plane.setCount(planeData.indexCount()) + .setIndexRange(sphereData.indexCount()); + GL::MeshView cone{mesh}; + cone.setCount(coneData.indexCount()) + .setIndexRange(sphereData.indexCount() + planeData.indexCount()); + + /* Some drivers have uniform offset alignment as high as 256, which means + the subsequent sets of uniforms have to be aligned to a multiply of it. + The data.uniformIncrement is set high enough to ensure that, in the + non-offset-bind case this value is 1. */ + + Containers::Array transformationProjectionData{2*data.uniformIncrement + 1}; + transformationProjectionData[0*data.uniformIncrement] = TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({-1.25f, -1.25f, 0.0f})* + Matrix4::rotationY(180.0_degf) /* so the texture is visible */ + ); + transformationProjectionData[1*data.uniformIncrement] = TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({ 1.25f, -1.25f, 0.0f}) + ); + transformationProjectionData[2*data.uniformIncrement] = TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({ 0.0f, 1.0f, 1.0f})* + Matrix4::rotationY(180.0_degf) /* so the texture is visible */ + ); + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, transformationProjectionData}; + + Containers::Array textureTransformationData{2*data.uniformIncrement + 1}; + textureTransformationData[0*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::translation({0.5f, 0.5f})* + Matrix3::rotation(180.0_degf)* + Matrix3::translation({-0.5f, -0.5f}) + ); + textureTransformationData[1*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix( + Matrix3::translation(Vector2::xAxis(1.0f))* + Matrix3::scaling(Vector2::xScale(-1.0f)) + ); + textureTransformationData[2*data.uniformIncrement] = TextureTransformationUniform{} + .setTextureMatrix(Matrix3{}); + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; + + Containers::Array drawData{2*data.uniformIncrement + 1}; + drawData[0*data.uniformIncrement] = VectorDrawUniform{} + .setColor(0x00ff00_rgbf) + .setBackgroundColor(0xccffcc_rgbf); + drawData[1*data.uniformIncrement] = VectorDrawUniform{} + .setColor(0xff0000_rgbf) + .setBackgroundColor(0xffcccc_rgbf); + drawData[2*data.uniformIncrement] = VectorDrawUniform{} + .setColor(0x00ff00_rgbf) + .setBackgroundColor(0xccffcc_rgbf); + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; + + VectorGL3D shader{VectorGL3D::Flag::UniformBuffers|VectorGL3D::Flag::TextureTransformation, data.drawCount}; + shader.bindVectorTexture(vector); + + /* Just one draw, rebinding UBOs each time */ + if(data.drawCount == 1) { + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 0*data.uniformIncrement*sizeof(TransformationProjectionUniform3D), + sizeof(TransformationProjectionUniform3D)); + shader.bindDrawBuffer(drawUniform, + 0*data.uniformIncrement*sizeof(VectorDrawUniform), + sizeof(VectorDrawUniform)); + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 0*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(sphere); + + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 1*data.uniformIncrement*sizeof(TransformationUniform3D), + sizeof(TransformationUniform3D)); + shader.bindDrawBuffer(drawUniform, + 1*data.uniformIncrement*sizeof(VectorDrawUniform), + sizeof(VectorDrawUniform)); + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 1*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(plane); + + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 2*data.uniformIncrement*sizeof(TransformationUniform3D), + sizeof(TransformationUniform3D)); + shader.bindDrawBuffer(drawUniform, + 2*data.uniformIncrement*sizeof(VectorDrawUniform), + sizeof(VectorDrawUniform)); + shader.bindTextureTransformationBuffer(textureTransformationUniform, + 2*data.uniformIncrement*sizeof(TextureTransformationUniform), + sizeof(TextureTransformationUniform)); + shader.draw(cone); + + /* Otherwise using the draw offset */ + } else { + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindTextureTransformationBuffer(textureTransformationUniform); + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } + + /* + - Sphere lower left, green, upside down + - Plane lower right, red, mirrored + - Cone up center, green + */ + MAGNUM_VERIFY_NO_GL_ERROR(); + 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, "VectorTestFiles", data.expected3D}), + (DebugTools::CompareImageToFile{_manager, data.maxThreshold, data.meanThreshold})); +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::VectorGLTest) diff --git a/src/Magnum/Shaders/Test/VectorTest.cpp b/src/Magnum/Shaders/Test/VectorTest.cpp new file mode 100644 index 000000000..ce3be11bf --- /dev/null +++ b/src/Magnum/Shaders/Test/VectorTest.cpp @@ -0,0 +1,118 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include + +#include "Magnum/Shaders/Vector.h" + +namespace Magnum { namespace Shaders { namespace Test { namespace { + +struct VectorTest: TestSuite::Tester { + explicit VectorTest(); + + template void uniformSize(); + + void drawUniformConstructDefault(); + void drawUniformConstructNoInit(); + void drawUniformSetters(); +}; + +VectorTest::VectorTest() { + addTests({&VectorTest::uniformSize, + + &VectorTest::drawUniformConstructDefault, + &VectorTest::drawUniformConstructNoInit, + &VectorTest::drawUniformSetters}); +} + +using namespace Math::Literals; + +template struct UniformTraits; +template<> struct UniformTraits { + static const char* name() { return "VectorDrawUniform"; } +}; + +template void VectorTest::uniformSize() { + setTestCaseTemplateName(UniformTraits::name()); + + CORRADE_FAIL_IF(sizeof(T) % sizeof(Vector4) != 0, sizeof(T) << "is not a multiple of vec4 for UBO alignment."); + + /* 48-byte structures are fine, we'll align them to 768 bytes and not + 256, but warn about that */ + CORRADE_FAIL_IF(768 % sizeof(T) != 0, sizeof(T) << "can't fit exactly into 768-byte UBO alignment."); + if(256 % sizeof(T) != 0) + CORRADE_WARN(sizeof(T) << "can't fit exactly into 256-byte UBO alignment, only 768."); +} + +void VectorTest::drawUniformConstructDefault() { + VectorDrawUniform a; + VectorDrawUniform b{DefaultInit}; + CORRADE_COMPARE(a.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(b.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(a.backgroundColor, 0x00000000_rgbaf); + CORRADE_COMPARE(b.backgroundColor, 0x00000000_rgbaf); + + constexpr VectorDrawUniform ca; + constexpr VectorDrawUniform cb{DefaultInit}; + CORRADE_COMPARE(ca.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(cb.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(ca.backgroundColor, 0x00000000_rgbaf); + CORRADE_COMPARE(cb.backgroundColor, 0x00000000_rgbaf); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void VectorTest::drawUniformConstructNoInit() { + /* Testing only some fields, should be enough */ + VectorDrawUniform a; + a.color = 0x354565fc_rgbaf; + a.backgroundColor = 0x98769facb_rgbaf; + + new(&a) VectorDrawUniform{NoInit}; + CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); + CORRADE_COMPARE(a.backgroundColor, 0x98769facb_rgbaf); + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void VectorTest::drawUniformSetters() { + VectorDrawUniform a; + a.setColor(0x354565fc_rgbaf) + .setBackgroundColor(0x98769facb_rgbaf); + CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); + CORRADE_COMPARE(a.backgroundColor, 0x98769facb_rgbaf); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Shaders::Test::VectorTest) diff --git a/src/Magnum/Shaders/Test/VectorTestFiles/multidraw2D-distancefield.tga b/src/Magnum/Shaders/Test/VectorTestFiles/multidraw2D-distancefield.tga new file mode 100644 index 0000000000000000000000000000000000000000..d255dcae9fc25c6abd695077d8ad09fffd3c766f GIT binary patch literal 3404 zcma)<%WGs+5XQfIJH8bR_*#ey7p`2XaWxBHJV1o1D>Rc4a(#0O>~v*;N! z4y34n_H(&Ree=;>eM}vE>szN zv`1Iz!`a!{uIEYk%ZMJ}y~cZy_Y;1gy;gXSK_7tUwZeN8`YCviw|L9Y2jN+Mfp;a{ zDD7I|fxHgRJ|3g&=b49I)lfYd&jY4SSP1zCrQs@1qq-NaRr@@qty*aVO14xKYot(; z27MlM*QHGaHgRacA}hNP-)e1%g&F-P-5kG9Eok0E1b}Svd0BCY+xGP+Oz=9emYUcW z!?sjwRyu8@y=Rc@n6u*~oHvR^dP(|B`rFbPULgG&)IZYg2*$X&Ebjon5MXaw$6!6Z z8m8vV!0~a;5SkNvZU!4FL{0whgT6%iK3I4cmTe68nSM?#;s@Q%y!X;YVs~=K@3Op8 zv9lMKN#7N`p0PZf8R?Di0OFeDMpnqylurCuoTzm}V|5<0l}>tQM_qt^U(wo~;{mZo9IyZunbY&KkZwDPkgz69@ zzXk_^JEdC-J?4~@vNV==dB)udReqe-JRxKJxb$p4j)l^wIw?VH{1m^m$6+3jcSWxLq674-<812nie@YsVKug4T%4(%7x)6uBbkh3hBtaO!QLZOr8P_il56;o*&Tz$&Z?x=s_Mm zG&1~3bl!3xZzq@3@Q%#$RbIKu$FD62ZUN(5|5u8rlWq{p{)M*p;CZ^IWs&L*^Rq>Dx1l&oql!@zLw@ z9K3^MoQ~ry3HOdlcU#>GcInub<Jdf7D0~;93(?N#XMcZx zUY{cWeiO|{{LeO!AIJCaQv2l#ibX+{e}$^j)Jn_L ziqyWuOLE59QkF-^Pg(B9xIFuT?(s&nL)g|BTNb%zVXHx+`wn zK!aRTC3c_0YyeVc!O#%SoC!u)z~0_bjFD2M!2$f<30^0|Qc{Y;W$oG@6UJNF3RtVHbET2V&Vs zJ1`w4-GM2vo1-XtDjZWNgkZ)5U0Km6=cov2P2Fiq3=;B%{WlRSl{Lg2JLG0ph&OR_ z4s#ySM(_xZWQVSnvd4mHor}JfO7hs1t^yD@8lCaOp`7^p12vD9j`LVW^DW+1QT06I zD#=#d#)j|`@R?0MeZtYBsMp1iW$ECcV!^il*a(*RgK~l*hIbI5Wk+Ts=@Y&?S3 zF7dNxF+7YKyBQtTtV&xYoA<=Xdy3>eS@NDXHR_xs_+$RVlO^w|l*gOn8pUmyf6>ZT z!_&5)JYA)mOP*F57Co)-|1pIr=A(}KKJ4R|jk9$xVatzi6e^a&#Jw7v%tBU%)yjDp z5SLI|#xf~$3%B@_h4UF%dww0nL23!-mwAe(UP6AvoGPHeby64(gT&n~4t%yvVGz-MA9WO2LKV#*HAT2;we1#!ja-F_4sP z(%n`Y1ZRQ+iW@}@f(RN>j6()QR8$agj?eEuw_erj>Mrn6r{2Bi{^$JXoI5?~WYy1R zZPwZPaCCI^|Hw{oQ>v6Or+EN*1SwGIe!Cn;N)O*B{VUX4knKQP49xJJL#6_>QMyGs zlQtk*ePld85JuJ(ww?e`*zG|BCXl3g@mJa$eONu*u$t{!t&@%!uoW>;(yOM}+5m5( zJsCrv899TL`o*q!7@J0%8ANRWb4YG#46TE;WDlF{VHPQLL!Wu!A-#zcpzR^)IqAjt zJxib1+91#YTIINKY5(l`26CZ8o9cv?eJ@KbYSkjTZ^05>H7vb=CVmdS&-V=dn6L+> z-#qN;Ep@zC)yWP6@nXDRdRh9Kww<$%nB|-C5zj*bS(D3ilM93&F&UoFjncg~nKW;Q zhJorN>8o%>MBAnFr0?lcplZFu6y@h|sA20%%TyFWBZBG&>7Q^#>_5f&IUt>H2Zj{d z6zYn4uMA;Sda1SxxP!$u#|+A(OZMVprO2Up1#}os3s2IXtBUr#lYp1yeaCU{w3Ym<6QJWgR_}s<}3|n$D02;>G>$vWHFTU>A%d{jEuT-%URRFFg?uhpbuI34{oIwYUaq$ce|;wnUO$dQI6@7<&}&)ZVBpP)UW3p!`3 zkGgB62Vjhq&qP(saV5hnN#~ftYq``}u^_lc^R+YN8;WUWYM(Z(Y4##>fD7-%gErlQ zSZi^43UgAs?4#8C(oM4K{1Il@KE%7r_kiE-Tk>RHW?SF9Ni3Lb<n0kboW36cfj>lR0#`OpgW5UBPps@wLaBT^j%2uP2bwohPn&63FQ95f(B@RL&A4` z2UIhV?gxD+%+h+*gXaTfbhI)y4 zK+<{517EAP%-twq2P1Y!L*NFCZBi3hKIZyLSUZ+E^HpCrYctS6Bw4TZKe09JhS`W) z;}>l%=~61k%gc0PU}mHGKvy?y($th@X0&aa!_c*ap$~lhoaWf{OT8S`=qa6QQA-|? z9thKBHG`pN9}DJgSkkRqRjVmYy`gR-jE{ST&e95w-7wbaJH5N8i>tL79<%lqLBx|@w3q>|F3S=k2dMs>&SB0M6!7ef&OFf*CbH;V)z#c_r$}7FG^VQ_KAag=)YRl9 zI#*aC?9)EX?Pao8dtd1l^iHgBS(j0oJH{b$hsTf2?X7#MbltjQDFzOW&&sjL@{{Dm zd03qG3HfooJ67oJ)iMKn7od8;(Bgl9z@>IBm3-}O!jCGOa-Zfzbs>T{yYZ4 zo>%ARJ>=tL881D^QVhIE*skrMdyynR*d-2!jt-OA?__RHZEYrX=KRO=4s{fshod?g zNzdy%+7hX49p2E|YO3DlsPYoghhf5{n9xLIJE(&zu#JwI%^?kt)08*vlk0{f9MduE zO&o9+jc-oi^Z>+ku!HfKk`TQ6W#}BobXwCVbfQjm5IUn$y5s>xj@c}xpY<92686yF zZ&v4S!`-`TZr0?a6@2aOUW|`^%sJPfta#Q`%c)%-QZ73=@kyT;^dp_v#p_aoA-s5D z7RNdn8}k)MS|E51(064qN7R)cwR`9vG4FN^c}U-FxqqMW1N3go{og%C@79tMvc+W8?&qU8E6> zu$pV1jjdjs!e!L1V&Q-e6vm!?ggB1_@vtG1Y}Cd)r9EMQB{Zmkpq2nF5Dly@G0 z+5RvPiUOFt*6TG|gTi63QwCT(PBv)6pqv=lg(vTr@KS#wUL)D1UAJ_rSjJjQV9cZw zG@RDyV%DoNY3{!e?<3@YH|%jBVrXsd**aAG9Rb6~IsTi&s7WN?Ju`N@V7IMj?0w)Y qnK8SER!<@J+pQq^*~?xHd3Z^Xytw%ng)Y&N&$e)(3c)c*%;iLihG literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp index 576698c78..692883dd9 100644 --- a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp @@ -23,9 +23,11 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include #include +#include #include "Magnum/DebugTools/CompareImage.h" #include "Magnum/Image.h" @@ -46,6 +48,17 @@ #include "Magnum/Trade/AbstractImporter.h" #include "Magnum/Trade/MeshData.h" +#ifndef MAGNUM_TARGET_GLES2 +#include "Magnum/GL/Extensions.h" +#include "Magnum/GL/MeshView.h" +#include "Magnum/MeshTools/Concatenate.h" +#include "Magnum/MeshTools/GenerateIndices.h" +#include "Magnum/Primitives/Cone.h" +#include "Magnum/Primitives/Plane.h" +#include "Magnum/Primitives/Square.h" +#include "Magnum/Shaders/Generic.h" +#endif + #include "configure.h" namespace Magnum { namespace Shaders { namespace Test { namespace { @@ -54,16 +67,38 @@ struct VertexColorGLTest: GL::OpenGLTester { explicit VertexColorGLTest(); template void construct(); + #ifndef MAGNUM_TARGET_GLES2 + template void constructUniformBuffers(); + #endif + template void constructMove(); + #ifndef MAGNUM_TARGET_GLES2 + template void constructMoveUniformBuffers(); + #endif + + #ifndef MAGNUM_TARGET_GLES2 + template void constructUniformBuffersZeroDraws(); + #endif + + #ifndef MAGNUM_TARGET_GLES2 + template void setUniformUniformBuffersEnabled(); + template void bindBufferUniformBuffersNotEnabled(); + template void setWrongDrawOffset(); + #endif void renderSetup(); void renderTeardown(); - template void renderDefaults2D(); - template void renderDefaults3D(); + template void renderDefaults2D(); + template void renderDefaults3D(); + + template void render2D(); + template void render3D(); - template void render2D(); - template void render3D(); + #ifndef MAGNUM_TARGET_GLES2 + void renderMulti2D(); + void renderMulti3D(); + #endif private: PluginManager::Manager _manager{"nonexistent"}; @@ -89,24 +124,115 @@ struct VertexColorGLTest: GL::OpenGLTester { using namespace Math::Literals; +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + VertexColorGL2D::Flags flags; + UnsignedInt drawCount; +} ConstructUniformBuffersData[]{ + {"classic fallback", {}, 1}, + {"", VertexColorGL2D::Flag::UniformBuffers, 1}, + {"multiple draws", VertexColorGL2D::Flag::UniformBuffers, 128} +}; +#endif + +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + const char* expected2D; + const char* expected3D; + UnsignedInt drawCount; + UnsignedInt uniformIncrement; + Float maxThreshold, meanThreshold; +} RenderMultiData[] { + {"bind with offset", "multidraw2D.tga", "multidraw3D.tga", + 1, 16, 0.0f, 0.0f}, + {"draw offset", "multidraw2D.tga", "multidraw3D.tga", + 3, 1, 0.0f, 0.0f} +}; +#endif + VertexColorGLTest::VertexColorGLTest() { addTests({ &VertexColorGLTest::construct<2>, - &VertexColorGLTest::construct<3>, + &VertexColorGLTest::construct<3>}); + + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({ + &VertexColorGLTest::constructUniformBuffers<2>, + &VertexColorGLTest::constructUniformBuffers<3>}, + Containers::arraySize(ConstructUniformBuffersData)); + #endif + + addTests({ &VertexColorGLTest::constructMove<2>, - &VertexColorGLTest::constructMove<3>}); + &VertexColorGLTest::constructMove<3>, - addTests({&VertexColorGLTest::renderDefaults2D, - &VertexColorGLTest::renderDefaults2D, - &VertexColorGLTest::renderDefaults3D, - &VertexColorGLTest::renderDefaults3D, + #ifndef MAGNUM_TARGET_GLES2 + &VertexColorGLTest::constructMoveUniformBuffers<2>, + &VertexColorGLTest::constructMoveUniformBuffers<3>, + #endif - &VertexColorGLTest::render2D, - &VertexColorGLTest::render2D, - &VertexColorGLTest::render3D, - &VertexColorGLTest::render3D}, + #ifndef MAGNUM_TARGET_GLES2 + &VertexColorGLTest::constructUniformBuffersZeroDraws<2>, + &VertexColorGLTest::constructUniformBuffersZeroDraws<3>, + #endif + + #ifndef MAGNUM_TARGET_GLES2 + &VertexColorGLTest::setUniformUniformBuffersEnabled<2>, + &VertexColorGLTest::setUniformUniformBuffersEnabled<3>, + &VertexColorGLTest::bindBufferUniformBuffersNotEnabled<2>, + &VertexColorGLTest::bindBufferUniformBuffersNotEnabled<3>, + &VertexColorGLTest::setWrongDrawOffset<2>, + &VertexColorGLTest::setWrongDrawOffset<3> + #endif + }); + + addTests({ + &VertexColorGLTest::renderDefaults2D, + #ifndef MAGNUM_TARGET_GLES2 + &VertexColorGLTest::renderDefaults2D, + #endif + &VertexColorGLTest::renderDefaults2D, + #ifndef MAGNUM_TARGET_GLES2 + &VertexColorGLTest::renderDefaults2D, + #endif + &VertexColorGLTest::renderDefaults3D, + #ifndef MAGNUM_TARGET_GLES2 + &VertexColorGLTest::renderDefaults3D, + #endif + &VertexColorGLTest::renderDefaults3D, + #ifndef MAGNUM_TARGET_GLES2 + &VertexColorGLTest::renderDefaults3D, + #endif + + &VertexColorGLTest::render2D, + #ifndef MAGNUM_TARGET_GLES2 + &VertexColorGLTest::render2D, + #endif + &VertexColorGLTest::render2D, + #ifndef MAGNUM_TARGET_GLES2 + &VertexColorGLTest::render2D, + #endif + &VertexColorGLTest::render3D, + #ifndef MAGNUM_TARGET_GLES2 + &VertexColorGLTest::render3D, + #endif + &VertexColorGLTest::render3D, + #ifndef MAGNUM_TARGET_GLES2 + &VertexColorGLTest::render3D, + #endif + }, + &VertexColorGLTest::renderSetup, + &VertexColorGLTest::renderTeardown); + + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({&VertexColorGLTest::renderMulti2D, + &VertexColorGLTest::renderMulti3D}, + Containers::arraySize(RenderMultiData), &VertexColorGLTest::renderSetup, &VertexColorGLTest::renderTeardown); + #endif /* Load the plugins directly from the build tree. Otherwise they're either static and already loaded or not present in the build tree */ @@ -147,6 +273,33 @@ template void VertexColorGLTest::construct() { MAGNUM_VERIFY_NO_GL_ERROR(); } +#ifndef MAGNUM_TARGET_GLES2 +template void VertexColorGLTest::constructUniformBuffers() { + setTestCaseTemplateName(std::to_string(dimensions)); + + auto&& data = ConstructUniformBuffersData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if((data.flags & VertexColorGL::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + VertexColorGL shader{data.flags, data.drawCount}; + CORRADE_COMPARE(shader.flags(), data.flags); + CORRADE_COMPARE(shader.drawCount(), data.drawCount); + CORRADE_VERIFY(shader.id()); + { + #ifdef CORRADE_TARGET_APPLE + CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); + #endif + CORRADE_VERIFY(shader.validate().first); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); +} +#endif + template void VertexColorGLTest::constructMove() { setTestCaseTemplateName(std::to_string(dimensions)); @@ -166,6 +319,121 @@ template void VertexColorGLTest::constructMove() { CORRADE_VERIFY(!b.id()); } +#ifndef MAGNUM_TARGET_GLES2 +template void VertexColorGLTest::constructMoveUniformBuffers() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + VertexColorGL a{VertexColorGL::Flag::UniformBuffers, 5}; + const GLuint id = a.id(); + CORRADE_VERIFY(id); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + VertexColorGL b{std::move(a)}; + CORRADE_COMPARE(b.id(), id); + CORRADE_COMPARE(b.flags(), VertexColorGL::Flag::UniformBuffers); + CORRADE_COMPARE(b.drawCount(), 5); + CORRADE_VERIFY(!a.id()); + + VertexColorGL c{NoCreate}; + c = std::move(b); + CORRADE_COMPARE(c.id(), id); + CORRADE_COMPARE(c.flags(), VertexColorGL::Flag::UniformBuffers); + CORRADE_COMPARE(c.drawCount(), 5); + CORRADE_VERIFY(!b.id()); +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +template void VertexColorGLTest::constructUniformBuffersZeroDraws() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + VertexColorGL{VertexColorGL::Flag::UniformBuffers, 0}; + CORRADE_COMPARE(out.str(), + "Shaders::VertexColorGL: draw count can't be zero\n"); +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +template void VertexColorGLTest::setUniformUniformBuffersEnabled() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + VertexColorGL shader{VertexColorGL::Flag::UniformBuffers}; + shader.setTransformationProjectionMatrix({}); + CORRADE_COMPARE(out.str(), + "Shaders::VertexColorGL::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled\n"); +} + +template void VertexColorGLTest::bindBufferUniformBuffersNotEnabled() { + 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}; + + GL::Buffer buffer; + VertexColorGL shader; + shader.bindTransformationProjectionBuffer(buffer) + .bindTransformationProjectionBuffer(buffer, 0, 16) + .setDrawOffset(0); + CORRADE_COMPARE(out.str(), + "Shaders::VertexColorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::VertexColorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::VertexColorGL::setDrawOffset(): the shader was not created with uniform buffers enabled\n"); +} + +template void VertexColorGLTest::setWrongDrawOffset() { + setTestCaseTemplateName(std::to_string(dimensions)); + + #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::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + std::ostringstream out; + Error redirectError{&out}; + VertexColorGL{VertexColorGL::Flag::UniformBuffers, 5} + .setDrawOffset(5); + CORRADE_COMPARE(out.str(), + "Shaders::VertexColorGL::setDrawOffset(): draw offset 5 is out of bounds for 5 draws\n"); +} +#endif + constexpr Vector2i RenderSize{80, 80}; void VertexColorGLTest::renderSetup() { @@ -193,8 +461,20 @@ void VertexColorGLTest::renderTeardown() { _color = GL::Renderbuffer{NoCreate}; } -template void VertexColorGLTest::renderDefaults2D() { - setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); +template void VertexColorGLTest::renderDefaults2D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == VertexColorGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"}); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } else + #endif + { + setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); + } Trade::MeshData circleData = Primitives::circle2DSolid(32, Primitives::Circle2DFlag::TextureCoordinates); @@ -207,8 +487,21 @@ template void VertexColorGLTest::renderDefaults2D() { GL::Mesh circle = MeshTools::compile(circleData); circle.addVertexBuffer(colors, 0, GL::Attribute{}); - VertexColorGL2D{} - .draw(circle); + VertexColorGL2D shader{flag}; + + if(flag == VertexColorGL2D::Flag{}) { + shader.draw(circle); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == VertexColorGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .draw(circle); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -230,8 +523,20 @@ template void VertexColorGLTest::renderDefaults2D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -template void VertexColorGLTest::renderDefaults3D() { - setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); +template void VertexColorGLTest::renderDefaults3D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == VertexColorGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"}); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } else + #endif + { + setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); + } if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) @@ -248,8 +553,21 @@ template void VertexColorGLTest::renderDefaults3D() { GL::Mesh sphere = MeshTools::compile(sphereData); sphere.addVertexBuffer(colors, 0, GL::Attribute{}); - VertexColorGL3D{} - .draw(sphere); + VertexColorGL3D shader{flag}; + + if(flag == VertexColorGL3D::Flag{}) { + shader.draw(sphere); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == VertexColorGL3D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform3D{} + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -267,8 +585,20 @@ template void VertexColorGLTest::renderDefaults3D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -template void VertexColorGLTest::render2D() { - setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); +template void VertexColorGLTest::render2D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == VertexColorGL2D::Flag::UniformBuffers) { + setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"}); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } else + #endif + { + setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); + } Trade::MeshData circleData = Primitives::circle2DSolid(32, Primitives::Circle2DFlag::TextureCoordinates); @@ -283,9 +613,23 @@ template void VertexColorGLTest::render2D() { GL::Mesh circle = MeshTools::compile(circleData); circle.addVertexBuffer(colors, 0, GL::Attribute{}); - VertexColorGL2D{} - .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + VertexColorGL2D shader{flag}; + + if(flag == VertexColorGL2D::Flag{}) { + shader.setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) .draw(circle); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == VertexColorGL2D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .draw(circle); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -308,8 +652,20 @@ template void VertexColorGLTest::render2D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } -template void VertexColorGLTest::render3D() { - setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); +template void VertexColorGLTest::render3D() { + #ifndef MAGNUM_TARGET_GLES2 + if(flag == VertexColorGL3D::Flag::UniformBuffers) { + setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"}); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + } else + #endif + { + setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4"); + } Trade::MeshData sphereData = Primitives::uvSphereSolid(16, 32, Primitives::UVSphereFlag::TextureCoordinates); @@ -324,13 +680,32 @@ template void VertexColorGLTest::render3D() { GL::Mesh sphere = MeshTools::compile(sphereData); sphere.addVertexBuffer(colors, 0, GL::Attribute{}); - VertexColorGL3D{} - .setTransformationProjectionMatrix( + VertexColorGL3D shader{flag}; + + if(flag == VertexColorGL3D::Flag{}) { + shader.setTransformationProjectionMatrix( Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* Matrix4::translation(Vector3::zAxis(-2.15f))* Matrix4::rotationY(-15.0_degf)* Matrix4::rotationX(15.0_degf)) .draw(sphere); + } + #ifndef MAGNUM_TARGET_GLES2 + else if(flag == VertexColorGL3D::Flag::UniformBuffers) { + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { + TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf) + ) + }}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .draw(sphere); + } + #endif + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); MAGNUM_VERIFY_NO_GL_ERROR(); @@ -353,6 +728,221 @@ template void VertexColorGLTest::render3D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } +#ifndef MAGNUM_TARGET_GLES2 +void VertexColorGLTest::renderMulti2D() { + auto&& data = RenderMultiData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + /* Circle is a fan, plane is a strip, make it indexed first */ + Trade::MeshData circleData = MeshTools::generateIndices(Primitives::circle2DSolid(32)); + Trade::MeshData squareData = MeshTools::generateIndices(Primitives::squareSolid()); + Trade::MeshData triangleData = MeshTools::generateIndices(Primitives::circle2DSolid(3)); + /* Concatenate the meshes, reserve a vertex color attribute and fill it + with a ... RAINBOW! */ + Trade::MeshData colored = MeshTools::interleave(MeshTools::concatenate({circleData, squareData, triangleData}), { + Trade::MeshAttributeData{Trade::MeshAttribute::Color, VertexFormat::Vector3, nullptr} + }); + Deg angle = 0.0_degf; + for(Vector3& i: colored.mutableAttribute(Trade::MeshAttribute::Color)) + i = Color3::fromHsv({angle += 360.0_degf/colored.vertexCount(), 1.0f, 1.0f}); + GL::Mesh mesh = MeshTools::compile(colored); + GL::MeshView circle{mesh}; + circle.setCount(circleData.indexCount()); + GL::MeshView square{mesh}; + square.setCount(squareData.indexCount()) + .setIndexRange(circleData.indexCount()); + GL::MeshView triangle{mesh}; + triangle.setCount(triangleData.indexCount()) + .setIndexRange(circleData.indexCount() + squareData.indexCount()); + + /* Some drivers have uniform offset alignment as high as 256, which means + the subsequent sets of uniforms have to be aligned to a multiply of it. + The data.uniformIncrement is set high enough to ensure that, in the + non-offset-bind case this value is 1. */ + + Containers::Array transformationProjectionData{2*data.uniformIncrement + 1}; + transformationProjectionData[0*data.uniformIncrement] = TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})* + Matrix3::translation({-1.25f, -1.25f}) + ); + transformationProjectionData[1*data.uniformIncrement] = TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})* + Matrix3::translation({ 1.25f, -1.25f}) + ); + transformationProjectionData[2*data.uniformIncrement] = TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix( + Matrix3::projection({2.1f, 2.1f})* + Matrix3::scaling(Vector2{0.4f})* + Matrix3::translation({ 0.00f, 1.25f}) + ); + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, transformationProjectionData}; + + VertexColorGL2D shader{VertexColorGL2D::Flag::UniformBuffers, data.drawCount}; + + /* Just one draw, rebinding UBOs each time */ + if(data.drawCount == 1) { + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 0*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), + sizeof(TransformationProjectionUniform2D)); + shader.draw(circle); + + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 1*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), + sizeof(TransformationProjectionUniform2D)); + shader.draw(square); + + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 2*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), + sizeof(TransformationProjectionUniform2D)); + shader.draw(triangle); + + /* Otherwise using the draw offset */ + } else { + shader.bindTransformationProjectionBuffer(transformationProjectionUniform); + shader.setDrawOffset(0) + .draw(circle); + shader.setDrawOffset(1) + .draw(square); + shader.setDrawOffset(2) + .draw(triangle); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + /* + - Circle should be lower left + - Square lower right + - Triangle up center + */ + 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, "VertexColorTestFiles", data.expected2D}), + (DebugTools::CompareImageToFile{_manager, data.maxThreshold, data.meanThreshold})); +} + +void VertexColorGLTest::renderMulti3D() { + auto&& data = RenderMultiData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + Trade::MeshData sphereData = Primitives::uvSphereSolid(16, 32); + /* Plane is a strip, make it indexed first */ + Trade::MeshData planeData = MeshTools::generateIndices(Primitives::planeSolid()); + Trade::MeshData coneData = Primitives::coneSolid(1, 32, 1.0f); + /* Concatenate the meshes, reserve a vertex color attribute and fill it + with a ... RAINBOW! */ + Trade::MeshData colored = MeshTools::interleave(MeshTools::concatenate({sphereData, planeData, coneData}), { + Trade::MeshAttributeData{Trade::MeshAttribute::Color, VertexFormat::Vector3, nullptr} + }); + Deg angle = 0.0_degf; + for(Vector3& i: colored.mutableAttribute(Trade::MeshAttribute::Color)) + i = Color3::fromHsv({angle += 360.0_degf/colored.vertexCount(), 1.0f, 1.0f}); + GL::Mesh mesh = MeshTools::compile(colored); + GL::MeshView sphere{mesh}; + sphere.setCount(sphereData.indexCount()); + GL::MeshView plane{mesh}; + plane.setCount(planeData.indexCount()) + .setIndexRange(sphereData.indexCount()); + GL::MeshView cone{mesh}; + cone.setCount(coneData.indexCount()) + .setIndexRange(sphereData.indexCount() + planeData.indexCount()); + + /* Some drivers have uniform offset alignment as high as 256, which means + the subsequent sets of uniforms have to be aligned to a multiply of it. + The data.uniformIncrement is set high enough to ensure that, in the + non-offset-bind case this value is 1. */ + + Containers::Array transformationProjectionData{2*data.uniformIncrement + 1}; + transformationProjectionData[0*data.uniformIncrement] = TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({-1.25f, -1.25f, 0.0f}) + ); + transformationProjectionData[1*data.uniformIncrement] = TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({ 1.25f, -1.25f, 0.0f}) + ); + transformationProjectionData[2*data.uniformIncrement] = TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::scaling(Vector3{0.4f})* + Matrix4::translation({ 0.0f, 1.0f, 1.0f}) + ); + GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, transformationProjectionData}; + + VertexColorGL3D shader{VertexColorGL3D::Flag::UniformBuffers, data.drawCount}; + + /* Just one draw, rebinding UBOs each time */ + if(data.drawCount == 1) { + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 0*data.uniformIncrement*sizeof(TransformationProjectionUniform3D), + sizeof(TransformationProjectionUniform3D)); + shader.draw(sphere); + + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 1*data.uniformIncrement*sizeof(TransformationProjectionUniform3D), + sizeof(TransformationProjectionUniform3D)); + shader.draw(plane); + + shader.bindTransformationProjectionBuffer(transformationProjectionUniform, + 2*data.uniformIncrement*sizeof(TransformationProjectionUniform3D), + sizeof(TransformationProjectionUniform3D)); + shader.draw(cone); + + /* Otherwise using the draw offset */ + } else { + shader.bindTransformationProjectionBuffer(transformationProjectionUniform); + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + /* + - Sphere should be lower left + - Plane lower right + - Cone up center + */ + 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, "VertexColorTestFiles", data.expected3D}), + (DebugTools::CompareImageToFile{_manager, data.maxThreshold, data.meanThreshold})); +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::VertexColorGLTest) diff --git a/src/Magnum/Shaders/Test/VertexColorGL_Test.cpp b/src/Magnum/Shaders/Test/VertexColorGL_Test.cpp index 34a8b27d9..1f86165ac 100644 --- a/src/Magnum/Shaders/Test/VertexColorGL_Test.cpp +++ b/src/Magnum/Shaders/Test/VertexColorGL_Test.cpp @@ -23,7 +23,9 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include "Magnum/Shaders/VertexColorGL.h" @@ -36,6 +38,9 @@ struct VertexColorGL_Test: TestSuite::Tester { template void constructNoCreate(); template void constructCopy(); + + void debugFlag(); + void debugFlags(); }; VertexColorGL_Test::VertexColorGL_Test() { @@ -44,7 +49,10 @@ VertexColorGL_Test::VertexColorGL_Test() { &VertexColorGL_Test::constructNoCreate<3>, &VertexColorGL_Test::constructCopy<2>, - &VertexColorGL_Test::constructCopy<3>}); + &VertexColorGL_Test::constructCopy<3>, + + &VertexColorGL_Test::debugFlag, + &VertexColorGL_Test::debugFlags}); } template void VertexColorGL_Test::constructNoCreate() { @@ -65,6 +73,30 @@ template void VertexColorGL_Test::constructCopy() { CORRADE_VERIFY(!std::is_copy_assignable>{}); } +void VertexColorGL_Test::debugFlag() { + std::ostringstream out; + + #ifndef MAGNUM_TARGET_GLES2 + Debug{&out} << VertexColorGL3D::Flag::UniformBuffers << VertexColorGL3D::Flag(0xf0); + CORRADE_COMPARE(out.str(), "Shaders::VertexColorGL::Flag::UniformBuffers Shaders::VertexColorGL::Flag(0xf0)\n"); + #else + Debug{&out} << VertexColorGL3D::Flag(0xf0); + CORRADE_COMPARE(out.str(), "Shaders::VertexColorGL::Flag(0xf0)\n"); + #endif +} + +void VertexColorGL_Test::debugFlags() { + std::ostringstream out; + + #ifndef MAGNUM_TARGET_GLES2 + Debug{&out} << (VertexColorGL3D::Flag::UniformBuffers|VertexColorGL3D::Flag(0xf0)) << VertexColorGL3D::Flags{}; + CORRADE_COMPARE(out.str(), "Shaders::VertexColorGL::Flag::UniformBuffers|Shaders::VertexColorGL::Flag(0xf0) Shaders::VertexColorGL::Flags{}\n"); + #else + Debug{&out} << VertexColorGL3D::Flag(0xf0) << VertexColorGL3D::Flags{}; + CORRADE_COMPARE(out.str(), "Shaders::VertexColorGL::Flag(0xf0) Shaders::VertexColorGL::Flags{}\n"); + #endif +} + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::VertexColorGL_Test) diff --git a/src/Magnum/Shaders/Test/VertexColorTestFiles/multidraw2D.tga b/src/Magnum/Shaders/Test/VertexColorTestFiles/multidraw2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..b1bb643d551c60e9563bc718f06db422dc9d7709 GIT binary patch literal 6852 zcmcJT33yahwuOHn0Yf4t1P}wHK^%c*P(cL&K}7@!AZ=(710te`i~X2|fefQaok2O}H#LbzTlT zb_X^mRyXAyEMClhOaaO?j3G+H7^0LY^cb&&YSa&(sO9-8vHa82lC@0=K4@_<`>r1* zwLU+p&AB0M&tZC?UR++z7BmD@4SMD#L| ztAq;(T_boMt%yJgegnQzyk&SyxXrlAaawWMa5%6#vAM8%DEDIVV-8@_C<|c>QyRgb z&|@4IsL>$!Q@s6HvSC-+)lY8EU)AdLf(|F9cRD)0%i$4S4`y~h)Ti6wZe72=zw@_u zcRby+!-WQI3X<+Ajl089vt_78OO+qfnSLIrF!T~~ndns_1%$5=x=yeNt(brTe_^xct_vHh9;+k5rj(K+MGc749Oe^*EN&^-SG|ev0o@ z9M{QKvy)b%qe6*kPX8XcVeVz*3ef^0*9aF9Dk4~nRzjc@zY(7auNjX8w-uKSrya%D z;ll35=EdrxJb*>R9K;l+EP_#?$N0XlMq{TlzO1-@;SaycKicZsyp9Lgci*+N_qN#s zHc!d?^DEEi4#`@VF??Om;kli%HntwJxq0UHTLuhvh`$V<8LtITIc^&+J5HUo!;RgG&4<-b zxrQZ(IfN-fnL>}T{+$|WmT;{iYx1RQ&CZ=|b^P#yf8EhDFX!paA7N9asw~34IAHV$WF{_)7%5CuC=9^#GnfSu~grO(u482f$NO9b69W^r}3{r?O zFVfE-|Ht&J$TgycM6MGqCR9SufMz66hTn|Og0~!x4YwVa1E&kc-QmIR!{*1TQ69t+ z!W_Y*&~I?R5eaoJwb3c7<^&dwqS~zj`wCU5v%$YoN;rRZG$M#%0w$sw_trt&j zzHs`j^X81XS_T@XLjT60pE@d`Iq)Aa9 zrmmcovhu^^<$tWVbW75Qd+PrF_)YI$s6E#ZH_Kb&y($H#kbhK$>qrsN5+Vk|rG&}| zn$XMy%JEzA+3-5>IB~mhd2s3q90BYawh-1Z<&_HNs(rKF(Hh&GDW4R#$jR@p>R68@ zdj~Aonl&qT?6l?6C(WHd`puQYM&&%4xut*a?R~rK?AvZ<|CZY`(>@*EX!Dqco8L;^ zG&g1AvRl@#zxksbiE9rgtU6m~d12gQN6pyFTra~(S&|Z@l&FzN8DTRa3&CW0Q9;wwZFW>+Ab6MsCe|e%<(ImSp#ty}0L;oX)Rozi-IF z*8R@h-u-G?J40Ga!yUgW?$E68(I!_1G`={zQT~Jm=VsRbZpkgj*WY|JFY(~foA&)s zdzT?D&sSqxm5yV`&nbxkF%m5!VkT@MR8G)_W+&jl@51NC>%rs0?Z>6z)O9$j>zL(9 zdb_mQgrC~JdZPQVz5NG&Haz2_iMN<1j18;0-GjeCk%%e?u=cjfyB)0J+-R^7B z+}%3e_DB(u;OdO}7Z)ez=iYp7N8+i&3CGXZK3W`iz*BQ?m5y(apIH(k zQbyE7#6q~7kd>ev%|XD0-;K|M*N4ZCJAf;QGsOSWG1ZeawzS#HKeZitqT8Uo{ri44 zyyr&~J1?HuVdm0%-`LQ6;xTXP6BTH9(-QBemnu(8mh&1oOB`Wr z^4!HX15bACy)Wa@tyvGQ8{ck8_Fc0Vr%%mk^y>DTha8OWcbc9Bbg|PVL|2{lqe4&N zaiLcg@qR*oVW99EAyfG64IM|2f2$-W#7wlDh?TI7kb|HT%|*b2-;2+WH-JaO9l}*z z$1s1wU{hMg)h=?pN>0*{CdNfJ6B9=`u@TO)7B)7+Z;b)H-nFnagnFWc*;XY zlnjY&19aAdglC1p!VqDokR=QkMhGw8&~X^~cS~YMEJUqDY=j+zoCIBH9s*wcK70YZ z8lE8TYC2M$4aN7fHhlcrosXRB_`s1TT7Q|@@{>_b)=o}cG&^zT()g*ljM>46BRu~D zgNqn!ICn9lh{;U!_D@QN^67$b}o#tRc~2swoO2Q{%ER-!f{cEV0VE`n|} zF99F^06qUmOoaX`co6x|3Q~^Ph}wxb3A+fn z33}0d1pN3ld_laGB4WQ$^&j>ob|_1`_sYF3&OCJ6;U`nR%uL+!a{Ri-F62!M)4XIy$(96Wh}y-rnZj&gjxbMnUzmSG!U5zzE`<%T6Lk=A5q1;u z5cHw>3269(_^L^0+CCh2kG+2Lq85!Wv`;?KJ#l|VeBKLuGM07OtX#^68F`iaIC z@mJb&?oo~pV*kthVG2W4e;a;vh!ukGu`yxjsv2PZCS;`kV zZ2O$8U$ONYww>dPE9^3|*TKO6M};H8VX4KT*iLNKm<;?4xc$cFKIk<-Xo7k7fz60z($-xVJUC0SDr`?&0l?=SIV374%DdSh!u2B9RzR9`#Ujr@Y^B~yJRifDWMJ#Y-f zG=h=|6wctv2V7di#gDo81(*KHl@k=6r=$RrF}C`q{&(`nIO;1=yO3YjP#@wasu8K= zsITG?ej49UT(4n!3-dfo%P{6({0!4B%!jdkhwDdtYN;dk-K?+TeTn?y_W6+j(Mn#i zHO!j`O+w4Y|9gC^@omJv9c>@sZ`8ddYNYQ({yz)~5c5B_wsGcXj&&-o=3U6`dgf>9 zEOw>V-AFq`T~{ZcR$h$17kNP6S|Kg;TbnCy)lpYEAw7_XkhaL3NK-vvQ)R$St&stDi9D?hP;yl%RNXOS0?*O9TvOUMvio8qDFDl3&K z%J;^|EqeN&D$`T%*O~Q2)LbKwamd@qMC28ny~0$tdLj>36{#7Ls`%+3Yb!w-cGMkI zRDY{aJsz2cyoXFd#^|umA^nkFNLNJ7SW(nlk$OnH4z;=xs*x(KnohZ_QeH+TAk&fe zky$!1bvg=pLAR;T-!wse)W1iuHddj^jaZRhaaTR2>PFSeBmN1!DoU8TK0v=! f_2Q|e3Z|~8SyX)0i>jt_P#K}F#NKEB>P`4Ru&%Bq literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/VertexColorTestFiles/multidraw3D.tga b/src/Magnum/Shaders/Test/VertexColorTestFiles/multidraw3D.tga new file mode 100644 index 0000000000000000000000000000000000000000..18c5b661d63ce113811f5421cac28ddacf3ae16d GIT binary patch literal 4828 zcma)A33OED75*jy$;<>uLN*9Uz_2B1#k5$9tWpt0OHn|qibb(lse%*)L^KPFtO8*d zWYby^+k=W$yV;g=sv;cL3Ic){P$*%EENMf&e*b+l&*?Dql=ENa@!q}Pckli0yZ3(p zAw>W5LqFtVYkGS6|0DCHmjZqY?#GdD@aZ}1K94OIu&EaFsAq3V=13ZXZ-TSIGH@!m z9{dE{0&WF&fqTI(QGE>is--$pQuekqH-hEwNZ!@;?kAFjs>4F%DLdn;9q6=yrevV*HLsARi+Rdv1ta=vKY=~aua3**$(Ui zb_2VEJ-}XIZwsqGI2e4$0u|YdvY!^=h>xHo62S~T&u+}LT%t8mL|jL7B3cq+3b9?- zmM%i5LA@Vr8Fmvfoq5^L~r6w;x1w^ zaTAeGWHT+B={d~Eb}KWR*_q5~!e;4g(TMqB79_I+_#N=(OF83%3@AuxxjZ}CU=Lyd zF_d_W7)6Y=C1yF&mME2k3oH+vUPxk!kHo|QKX6XE}n7( zF@Y!}W)LqB6;zEo;(Li+qLifeB)St_iO$3gL@}YeSQUDyB=te!0pfm3`#_>U(bx8(n{9p(uV~4}5z5lb z1RA9bEhg3z?-3smeP1rkF=h z8M`vjnGk8Yq%1+0r7@&=+b%ew!&NwX9LK-LiKD2h#EB!0_Y>kBTkjHDx_+B@i%{m3 zgx85zi8aJ>Vj(e$C?SSjLL3|Ie-T$!;pA61dl)qbQM(@(KLhumZV#%C;6OFDU&Qu0 z?C=(`o!CZf)iw|x5}RySes2!Rk5xt=iMTvL#3eBihiWQ8k!=G%0ykM(`~#?0>K`~+ zi9@FlIC#?ilt@w!oyMUvI9QF(&*H#&?61MTTI?lu($#`WW1SyyKwVwj(ZY%iJeEI? z+rdA9YQ1lOzXsQWtH2eY8u4zNI*gNF;N&5k(%)a8O6xFA9L7Jt#PK6I_7%Q9j-yqm z)Sg|yK~+pVf1(`^D1N2Ky-H5<+$d19UJA|wXM)qf$?_arXnk&{n?!XxQMUsZZPo6; zzjvZ$Cob&5x!pLs2WLLR>CaImr{qp;wAxCPf+&A%{4xBl+QXb~Q)3MH1gKH+Gw@;X zNpoj|C1RthX(PfeCz^S3|9$X};0NH}zz1Er>~2SF4MfC=Hr}y0Y0A;yPi;*0 z2k!v;fVY6R*+`iKF1ED@)YA15%gu|nE7}|FkS3^hOMCX0C}rtoVs&(G`h*-oXYjk$ z)eGUKTbqEpsR=qB9EVa#$X!kVCxVl}=WLQ{H)h#Ztc&G}53fvl`If6=k{i;kQrV(0 z^U~Q=!zGLP$!?eq1P6#~0D|svkj>Jc*mzSy9sx&yV;qrd|78h+iW0f89b#@4)0(qk zKEqcr*p?~nsoByz%wq6*XnvMRQrx9(DD+O?jo?i-Q*U)FZk#%n_;5-i;kctmCQ)b| zTLVl}}*+FJCVAd7P2J>8!BOK)kB8hUiV^feAL_BOW zb0jgE7|x_(1`C-|$Y4g7`>EB9XYWUW&~~ zwZ|32T5FgZ7$k{|W56DwJb}gYo(qOaBZ(tRwPNDid&J+LAU6(8ekSL)s0I?_J zMKZ}BW@k~A*bu9=`@#D_)t%P8R)hDL!ZM5(8`U25=KTtM#Tu4g z#MRMmDUOAb#gZ#M7N5eyA8fC?>RnYYAJlASn5`)O2fjSGycAYGd&tYo^F{Wd5y4-o z-)73TkYizmspGVouGV8GDz`OgXHCEVhrA`IZ^ywIO@3o~u4)U+{Ko z%|ay*jHRvd(B6gvQnh9%b8dZ`7_lfUc@8A5s2LA9!2P4)1$L}ZmR4R zi-(}(%(VPWv)7@_c0Yo%LcbY8kORK?jW&o>e$te!mZF$$z6n;bl!DJ%M9vFC=w{}k zmhJb!8*G{lw<+!0%KyoRsj7a)wpG>+;U!7h56rNW8EP07TRBNhK6{4ZmEm(SwvO3s zMJmScnN>$OSzXyoFgnj$8BPM9b|(pUx{;l;T`6fKuIs_;Y+Y;R@stH96UC@{_N=ta z5jL}udoq#!HDHl7YAHxxEHW8yM7s01Z?#I+snWk1)cK~pbuDeThcA8A{?nJGQ7_Xvyc`>B_nn2^ZvuvmsK0;z6+KHzvGhi?>`tn+m&l#i J`FpwR{{VP)C4&F} literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Vector.frag b/src/Magnum/Shaders/Vector.frag index 8f0f63795..94f0bb419 100644 --- a/src/Magnum/Shaders/Vector.frag +++ b/src/Magnum/Shaders/Vector.frag @@ -29,8 +29,13 @@ #define texture texture2D #endif +#ifndef RUNTIME_CONST +#define const +#endif + /* Uniforms */ +#ifndef UNIFORM_BUFFERS #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 2) #endif @@ -45,6 +50,33 @@ uniform lowp vec4 color #endif ; +/* Uniform buffers */ + +#else +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 0) +#endif +uniform highp uint drawOffset + #ifndef GL_ES + = 0u + #endif + ; + +struct DrawUniform { + lowp vec4 color; + lowp vec4 backgroundColor; + lowp uvec4 reserved; +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 2 + #endif +) uniform Draw { + DrawUniform draws[DRAW_COUNT]; +}; +#endif + /* Textures */ #ifdef EXPLICIT_BINDING @@ -66,6 +98,11 @@ out lowp vec4 fragmentColor; #endif void main() { + #ifdef UNIFORM_BUFFERS + lowp const vec4 color = draws[drawOffset].color; + lowp const vec4 backgroundColor = draws[drawOffset].backgroundColor; + #endif + lowp float intensity = texture(vectorTexture, interpolatedTextureCoordinates).r; fragmentColor = mix(backgroundColor, color, intensity); } diff --git a/src/Magnum/Shaders/Vector.h b/src/Magnum/Shaders/Vector.h index e73d13435..f110efbfb 100644 --- a/src/Magnum/Shaders/Vector.h +++ b/src/Magnum/Shaders/Vector.h @@ -25,26 +25,104 @@ DEALINGS IN THE SOFTWARE. */ -#ifdef MAGNUM_BUILD_DEPRECATED /** @file - * @brief Typedef @ref Magnum::Shaders::Vector, alias @ref Magnum::Shaders::Vector2D, @ref Magnum::Shaders::Vector3D - * @m_deprecated_since_latest Use @ref Magnum/Shaders/VectorGL.h, the - * @ref Magnum::Shaders::VectorGL "VectorGL" class and - * related typedefs instead. + * @brief Struct @ref Magnum::Shaders::VectorDrawUniform */ -#endif -#include "Magnum/configure.h" +#include "Magnum/Magnum.h" +#include "Magnum/Math/Color.h" #ifdef MAGNUM_BUILD_DEPRECATED #include #include "Magnum/Shaders/VectorGL.h" - -CORRADE_DEPRECATED_FILE("use Magnum/Shaders/VectorGL.h, the VectorGL class and related typedefs instead") +#endif namespace Magnum { namespace Shaders { +/** +@brief Per-draw uniform for vector shaders +@m_since_latest + +Together with the generic @ref TransformationProjectionUniform2D / +@ref TransformationProjectionUniform3D contains parameters that are specific to +each draw call. Texture transformation, if needed, is supplied separately in a +@ref TextureTransformationUniform. +@see @ref VectorGL::bindDrawBuffer() +*/ +struct VectorDrawUniform { + /** @brief Construct with default parameters */ + constexpr explicit VectorDrawUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f}, backgroundColor{0.0f, 0.0f, 0.0f, 0.0f} {} + + /** @brief Construct without initializing the contents */ + explicit VectorDrawUniform(NoInitT) noexcept: color{NoInit}, backgroundColor{NoInit} {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref color field + * @return Reference to self (for method chaining) + */ + VectorDrawUniform& setColor(const Color4& color) { + this->color = color; + return *this; + } + + /** + * @brief Set the @ref backgroundColor field + * @return Reference to self (for method chaining) + */ + VectorDrawUniform& setBackgroundColor(const Color4& color) { + backgroundColor = color; + return *this; + } + + /** + * @} + */ + + /** + * @brief Fill color + * + * Default is @cpp 0xffffffff_rgbaf @ce. + * @see @ref VectorGL::setColor() + */ + Color4 color; + + /** + * @brief Background color + * + * Default is @cpp 0x00000000_rgbaf @ce. + * @see @ref VectorGL::setBackgroundColor() + */ + Color4 backgroundColor; + + /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + /* This field is an UnsignedInt in the shader and skinOffset is extracted + as (value >> 16), so the order has to be different on BE */ + #ifndef CORRADE_TARGET_BIG_ENDIAN + UnsignedShort:16; + UnsignedShort:16; /* reserved for skinOffset */ + #else + UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort:16; + #endif + Int:32; /* reserved for objectId */ + Int:32; /* reserved for alphaMask */ + Int:32; + #endif +}; + +#ifdef MAGNUM_BUILD_DEPRECATED /** @brief @copybrief Shaders::VectorGL * @m_deprecated_since_latest Use @ref Shaders::VectorGL "VectorGL" instead. */ @@ -61,10 +139,8 @@ typedef CORRADE_DEPRECATED("use VectorGL2D instead") VectorGL2D Vector2D; * @m_deprecated_since_latest Use @ref VectorGL3D instead. */ typedef CORRADE_DEPRECATED("use VectorGL3D instead") VectorGL3D Vector3D; +#endif }} -#else -#error use Magnum/Shaders/VectorGL.h, the VectorGL class and related typedefs instead -#endif #endif diff --git a/src/Magnum/Shaders/Vector.vert b/src/Magnum/Shaders/Vector.vert index 17cb41708..e0fbab898 100644 --- a/src/Magnum/Shaders/Vector.vert +++ b/src/Magnum/Shaders/Vector.vert @@ -28,8 +28,13 @@ #define out varying #endif +#ifndef RUNTIME_CONST +#define const +#endif + /* Uniforms */ +#ifndef UNIFORM_BUFFERS #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -60,6 +65,51 @@ uniform mediump mat3 textureMatrix ; #endif +/* Uniform buffers */ + +#else +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 0) +#endif +uniform highp uint drawOffset + #ifndef GL_ES + = 0u + #endif + ; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 1 + #endif +) uniform TransformationProjection { + highp + #ifdef TWO_DIMENSIONS + mat3 + #elif defined(THREE_DIMENSIONS) + mat4 + #else + #error + #endif + transformationProjectionMatrices[DRAW_COUNT]; +}; + +#ifdef TEXTURE_TRANSFORMATION +struct TextureTransformationUniform { + highp vec4 rotationScaling; + highp vec4 offsetReservedReserved; + #define textureTransformation_offset offsetReservedReserved.xy +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 3 + #endif +) uniform TextureTransformation { + TextureTransformationUniform textureTransformations[DRAW_COUNT]; +}; +#endif +#endif + /* Inputs */ #ifdef EXPLICIT_ATTRIB_LOCATION @@ -83,6 +133,21 @@ in mediump vec2 textureCoordinates; out mediump vec2 interpolatedTextureCoordinates; void main() { + #ifdef UNIFORM_BUFFERS + highp const + #ifdef TWO_DIMENSIONS + mat3 + #elif defined(THREE_DIMENSIONS) + mat4 + #else + #error + #endif + transformationProjectionMatrix = transformationProjectionMatrices[drawOffset]; + #ifdef TEXTURE_TRANSFORMATION + mediump const mat3 textureMatrix = mat3(textureTransformations[drawOffset].rotationScaling.xy, 0.0, textureTransformations[drawOffset].rotationScaling.zw, 0.0, textureTransformations[drawOffset].textureTransformation_offset, 1.0); + #endif + #endif + #ifdef TWO_DIMENSIONS gl_Position.xywz = vec4(transformationProjectionMatrix*vec3(position, 1.0), 0.0); #elif defined(THREE_DIMENSIONS) diff --git a/src/Magnum/Shaders/VectorGL.cpp b/src/Magnum/Shaders/VectorGL.cpp index 214f04ab6..e0c2531af 100644 --- a/src/Magnum/Shaders/VectorGL.cpp +++ b/src/Magnum/Shaders/VectorGL.cpp @@ -37,15 +37,51 @@ #include "Magnum/Math/Matrix3.h" #include "Magnum/Math/Matrix4.h" +#ifndef MAGNUM_TARGET_GLES2 +#include + +#include "Magnum/GL/Buffer.h" +#endif + #include "Magnum/Shaders/Implementation/CreateCompatibilityShader.h" namespace Magnum { namespace Shaders { namespace { enum: Int { TextureUnit = 6 }; + + #ifndef MAGNUM_TARGET_GLES2 + enum: Int { + /* Not using the zero binding to avoid conflicts with + ProjectionBufferBinding from other shaders which can likely stay + bound to the same buffer for the whole time */ + TransformationProjectionBufferBinding = 1, + DrawBufferBinding = 2, + TextureTransformationBufferBinding = 3 + }; + #endif } -template VectorGL::VectorGL(const Flags flags): _flags{flags} { +template VectorGL::VectorGL(const Flags flags + #ifndef MAGNUM_TARGET_GLES2 + , const UnsignedInt drawCount + #endif +): + _flags{flags} + #ifndef MAGNUM_TARGET_GLES2 + , _drawCount{drawCount} + #endif +{ + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || drawCount, + "Shaders::VectorGL: draw count can't be zero", ); + #endif + + #ifndef MAGNUM_TARGET_GLES + if(flags >= Flag::UniformBuffers) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); + #endif + #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ if(!Utility::Resource::hasGroup("MagnumShadersGL")) @@ -65,9 +101,25 @@ template VectorGL::VectorGL(const Flags flag GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment); vert.addSource(flags & Flag::TextureTransformation ? "#define TEXTURE_TRANSFORMATION\n" : "") - .addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n" : "#define THREE_DIMENSIONS\n") - .addSource(rs.get("generic.glsl")) + .addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n" : "#define THREE_DIMENSIONS\n"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + vert.addSource(Utility::formatString( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n", + drawCount)); + } + #endif + vert.addSource(rs.get("generic.glsl")) .addSource(rs.get("Vector.vert")); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + frag.addSource(Utility::formatString( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n", + drawCount)); + } + #endif frag.addSource(rs.get("generic.glsl")) .addSource(rs.get("Vector.frag")); @@ -92,11 +144,18 @@ template VectorGL::VectorGL(const Flags flag if(!context.isExtensionSupported(version)) #endif { - _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); - if(flags & Flag::TextureTransformation) - _textureMatrixUniform = uniformLocation("textureMatrix"); - _backgroundColorUniform = uniformLocation("backgroundColor"); - _colorUniform = uniformLocation("color"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + _drawOffsetUniform = uniformLocation("drawOffset"); + } else + #endif + { + _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); + if(flags & Flag::TextureTransformation) + _textureMatrixUniform = uniformLocation("textureMatrix"); + _backgroundColorUniform = uniformLocation("backgroundColor"); + _colorUniform = uniformLocation("color"); + } } #ifndef MAGNUM_TARGET_GLES @@ -104,23 +163,51 @@ template VectorGL::VectorGL(const Flags flag #endif { setUniform(uniformLocation("vectorTexture"), TextureUnit); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + setUniformBlockBinding(uniformBlockIndex("TransformationProjection"), TransformationProjectionBufferBinding); + setUniformBlockBinding(uniformBlockIndex("Draw"), DrawBufferBinding); + if(flags & Flag::TextureTransformation) + setUniformBlockBinding(uniformBlockIndex("TextureTransformation"), TextureTransformationBufferBinding); + } + #endif } /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ #ifdef MAGNUM_TARGET_GLES - setTransformationProjectionMatrix(MatrixTypeFor{Math::IdentityInit}); - if(flags & Flag::TextureTransformation) - setTextureMatrix(Matrix3{Math::IdentityInit}); - setColor(Color4{1.0f}); /* Background color is zero by default */ + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + /* Draw offset is zero by default */ + } else + #endif + { + setTransformationProjectionMatrix(MatrixTypeFor{Math::IdentityInit}); + if(flags & Flag::TextureTransformation) + setTextureMatrix(Matrix3{Math::IdentityInit}); + /* Background color is zero by default */ + setColor(Color4{1.0f}); + } #endif } +#ifndef MAGNUM_TARGET_GLES2 +template VectorGL::VectorGL(const Flags flags): VectorGL{flags, 1} {} +#endif + template VectorGL& VectorGL::setTransformationProjectionMatrix(const MatrixTypeFor& matrix) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::VectorGL::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_transformationProjectionMatrixUniform, matrix); return *this; } template VectorGL& VectorGL::setTextureMatrix(const Matrix3& matrix) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::VectorGL::setTextureMatrix(): the shader was created with uniform buffers enabled", *this); + #endif CORRADE_ASSERT(_flags & Flag::TextureTransformation, "Shaders::VectorGL::setTextureMatrix(): the shader was not created with texture transformation enabled", *this); setUniform(_textureMatrixUniform, matrix); @@ -128,15 +215,80 @@ template VectorGL& VectorGL::set } template VectorGL& VectorGL::setBackgroundColor(const Color4& color) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::VectorGL::setBackgroundColor(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_backgroundColorUniform, color); return *this; } template VectorGL& VectorGL::setColor(const Color4& color) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::VectorGL::setColor(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_colorUniform, color); return *this; } +#ifndef MAGNUM_TARGET_GLES2 +template VectorGL& VectorGL::setDrawOffset(const UnsignedInt offset) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::VectorGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(offset < _drawCount, + "Shaders::VectorGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); + setUniform(_drawOffsetUniform, offset); + return *this; +} + +template VectorGL& VectorGL::bindTransformationProjectionBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::VectorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); + return *this; +} + +template VectorGL& VectorGL::bindTransformationProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::VectorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); + return *this; +} + +template VectorGL& VectorGL::bindDrawBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::VectorGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding); + return *this; +} + +template VectorGL& VectorGL::bindDrawBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::VectorGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); + return *this; +} + +template VectorGL& VectorGL::bindTextureTransformationBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::VectorGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureTransformation, + "Shaders::VectorGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding); + return *this; +} + +template VectorGL& VectorGL::bindTextureTransformationBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::VectorGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureTransformation, + "Shaders::VectorGL::bindTextureTransformationBuffer(): the shader was not created with texture transformation enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); + return *this; +} +#endif + template VectorGL& VectorGL::bindVectorTexture(GL::Texture2D& texture) { texture.bind(TextureUnit); return *this; @@ -154,6 +306,9 @@ Debug& operator<<(Debug& debug, const VectorGLFlag value) { /* LCOV_EXCL_START */ #define _c(v) case VectorGLFlag::v: return debug << "::" #v; _c(TextureTransformation) + #ifndef MAGNUM_TARGET_GLES2 + _c(UniformBuffers) + #endif #undef _c /* LCOV_EXCL_STOP */ } @@ -163,8 +318,11 @@ Debug& operator<<(Debug& debug, const VectorGLFlag value) { Debug& operator<<(Debug& debug, const VectorGLFlags value) { return Containers::enumSetDebugOutput(debug, value, "Shaders::VectorGL::Flags{}", { - VectorGLFlag::TextureTransformation - }); + VectorGLFlag::TextureTransformation, + #ifndef MAGNUM_TARGET_GLES2 + VectorGLFlag::UniformBuffers + #endif + }); } } diff --git a/src/Magnum/Shaders/VectorGL.h b/src/Magnum/Shaders/VectorGL.h index dd5694fc4..c899e5130 100644 --- a/src/Magnum/Shaders/VectorGL.h +++ b/src/Magnum/Shaders/VectorGL.h @@ -39,7 +39,10 @@ namespace Magnum { namespace Shaders { namespace Implementation { enum class VectorGLFlag: UnsignedByte { - TextureTransformation = 1 << 0 + TextureTransformation = 1 << 0, + #ifndef MAGNUM_TARGET_GLES2 + UniformBuffers = 1 << 1 + #endif }; typedef Containers::EnumSet VectorGLFlags; } @@ -117,7 +120,23 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * @see @ref setTextureMatrix() * @m_since{2020,06} */ - TextureTransformation = 1 << 0 + TextureTransformation = 1 << 0, + + #ifndef MAGNUM_TARGET_GLES2 + /** + * Use uniform buffers. Expects that uniform data are supplied via + * @ref bindTransformationProjectionBuffer(), + * @ref bindDrawBuffer() and @ref bindTextureTransformationBuffer() + * instead of direct uniform setters. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES + * 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL + * 1.0. + * @m_since_latest + */ + UniformBuffers = 1 << 1 + #endif }; /** @@ -137,9 +156,44 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL /** * @brief Constructor * @param flags Flags + * + * While this function is meant mainly for the classic uniform + * scenario (without @ref Flag::UniformBuffers set), it's equivalent to + * @ref VectorGL(Flags, UnsignedInt) with @p drawCount set to @cpp 1 @ce. */ explicit VectorGL(Flags flags = {}); + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Construct for a multi-draw scenario + * @param flags Flags + * @param drawCount Size of a @ref TransformationProjectionUniform2D + * / @ref TransformationProjectionUniform3D / + * @ref VectorDrawUniform / @ref TextureTransformationUniform + * buffer bound with @ref bindTransformationProjectionBuffer(), + * @ref bindDrawBuffer() and @ref bindTextureTransformationBuffer() + * + * If @p flags contains @ref Flag::UniformBuffers @p drawCount + * describes the uniform buffer sizes as these are required to have a + * statically defined size. The draw offset is then set via + * @ref setDrawOffset(). + * + * If @p flags don't contain @ref Flag::UniformBuffers, @p drawCount is + * ignored and the constructor behaves the same as @ref VectorGL(Flags). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + /** @todo this constructor will eventually need to have also joint + count, per-vertex weight count, view count for multiview and clip + plane count ... and putting them in arbitrary order next to each + other is too error-prone, so it needs some other solution + (accepting pairs of parameter type and value like in GL context + creation, e.g., which will probably need a new enum as reusing Flag + for this might be too confusing) */ + explicit VectorGL(Flags flags, UnsignedInt drawCount); + #endif + /** * @brief Construct without creating the underlying OpenGL object * @@ -172,8 +226,26 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL */ Flags flags() const { return _flags; } + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Draw count + * @m_since_latest + * + * Statically defined size of each of the + * @ref TransformationProjectionUniform2D / + * @ref TransformationProjectionUniform3D, @ref VectorDrawUniform and + * @ref TextureTransformationUniform uniform buffers. Has use only if + * @ref Flag::UniformBuffers is set. + * @requires_gles30 Not defined on OpenGL ES 2.0 builds. + * @requires_webgl20 Not defined on WebGL 1.0 builds. + */ + UnsignedInt drawCount() const { return _drawCount; } + #endif + /** @{ * @name Uniform setters + * + * Used only if @ref Flag::UniformBuffers is not set. */ /** @@ -181,6 +253,11 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * @return Reference to self (for method chaining) * * Initial value is an identity matrix. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref TransformationProjectionUniform2D::transformationProjectionMatrix / + * @ref TransformationProjectionUniform3D::transformationProjectionMatrix + * and call @ref bindTransformationProjectionBuffer() instead. */ VectorGL& setTransformationProjectionMatrix(const MatrixTypeFor& matrix); @@ -192,6 +269,11 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * Expects that the shader was created with * @ref Flag::TextureTransformation enabled. Initial value is an * identity matrix. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref TextureTransformationUniform::rotationScaling and + * @ref TextureTransformationUniform::offset and call + * @ref bindTextureTransformationBuffer() instead. */ VectorGL& setTextureMatrix(const Matrix3& matrix); @@ -200,6 +282,10 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * @return Reference to self (for method chaining) * * Initial value is @cpp 0x00000000_rgbaf @ce. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref VectorDrawUniform::backgroundColor and call + * @ref bindDrawBuffer() instead. * @see @ref setColor() */ VectorGL& setBackgroundColor(const Color4& color); @@ -209,6 +295,10 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * @return Reference to self (for method chaining) * * Initial value is @cpp 0xffffffff_rgbaf @ce. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref VectorDrawUniform::color and call @ref bindDrawBuffer() + * instead. * @see @ref setBackgroundColor() */ VectorGL& setColor(const Color4& color); @@ -217,6 +307,98 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * @} */ + #ifndef MAGNUM_TARGET_GLES2 + /** @{ + * @name Uniform buffer binding and related uniform setters + * + * Used if @ref Flag::UniformBuffers is set. + */ + + /** + * @brief Set a draw offset + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Specifies which item in the @ref TransformationProjectionUniform2D / + * @ref TransformationProjectionUniform3D, + * @ref VectorDrawUniform and @ref TextureTransformationUniform buffers + * bound with @ref bindTransformationProjectionBuffer(), + * @ref bindDrawBuffer() and @ref bindTextureTransformationBuffer() + * should be used for current draw. Expects that + * @ref Flag::UniformBuffers is set and @p offset is less than + * @ref drawCount(). Initial value is @cpp 0 @ce. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + VectorGL& setDrawOffset(UnsignedInt offset); + + /** + * @brief Set a transformation and projection uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref drawCount() instances of + * @ref TransformationProjectionUniform2D / + * @ref TransformationProjectionUniform3D. At the very least you need + * to call also @ref bindDrawBuffer(). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + VectorGL& bindTransformationProjectionBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + VectorGL& bindTransformationProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a draw uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref drawCount() instances of + * @ref VectorDrawUniform. At the very least you need to call also + * @ref bindTransformationProjectionBuffer(). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + VectorGL& bindDrawBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + VectorGL& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @brief Set a texture transformation uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that both @ref Flag::UniformBuffers and + * @ref Flag::TextureTransformation is set. The buffer is expected to + * contain @ref drawCount() instances of + * @ref TextureTransformationUniform. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + VectorGL& bindTextureTransformationBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + VectorGL& bindTextureTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @} + */ + #endif + /** @{ * @name Texture binding */ @@ -243,10 +425,18 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL #endif Flags _flags; + #ifndef MAGNUM_TARGET_GLES2 + UnsignedInt _drawCount{}; + #endif Int _transformationProjectionMatrixUniform{0}, _textureMatrixUniform{1}, _backgroundColorUniform{2}, _colorUniform{3}; + #ifndef MAGNUM_TARGET_GLES2 + /* Used instead of all other uniforms when Flag::UniformBuffers is set, + so it can alias them */ + Int _drawOffsetUniform{0}; + #endif }; /** diff --git a/src/Magnum/Shaders/VertexColor.vert b/src/Magnum/Shaders/VertexColor.vert index fda6574e3..c7597a413 100644 --- a/src/Magnum/Shaders/VertexColor.vert +++ b/src/Magnum/Shaders/VertexColor.vert @@ -28,8 +28,13 @@ #define out varying #endif +#ifndef RUNTIME_CONST +#define const +#endif + /* Uniforms */ +#ifndef UNIFORM_BUFFERS #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -49,6 +54,35 @@ uniform highp mat4 transformationProjectionMatrix #error #endif +/* Uniform buffers */ + +#else +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 0) +#endif +uniform highp uint drawOffset + #ifndef GL_ES + = 0u + #endif + ; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 1 + #endif +) uniform TransformationProjection { + highp + #ifdef TWO_DIMENSIONS + mat3 + #elif defined(THREE_DIMENSIONS) + mat4 + #else + #error + #endif + transformationProjectionMatrices[DRAW_COUNT]; +}; +#endif + /* Inputs */ #ifdef EXPLICIT_ATTRIB_LOCATION @@ -72,6 +106,18 @@ in lowp vec4 color; out lowp vec4 interpolatedColor; void main() { + #ifdef UNIFORM_BUFFERS + highp const + #ifdef TWO_DIMENSIONS + mat3 + #elif defined(THREE_DIMENSIONS) + mat4 + #else + #error + #endif + transformationProjectionMatrix = transformationProjectionMatrices[drawOffset]; + #endif + #ifdef TWO_DIMENSIONS gl_Position.xywz = vec4(transformationProjectionMatrix*vec3(position, 1.0), 0.0); #elif defined(THREE_DIMENSIONS) diff --git a/src/Magnum/Shaders/VertexColorGL.cpp b/src/Magnum/Shaders/VertexColorGL.cpp index 66ef23fc1..4c1b4eb3f 100644 --- a/src/Magnum/Shaders/VertexColorGL.cpp +++ b/src/Magnum/Shaders/VertexColorGL.cpp @@ -25,6 +25,7 @@ #include "VertexColorGL.h" +#include #include #include @@ -35,11 +36,47 @@ #include "Magnum/Math/Matrix3.h" #include "Magnum/Math/Matrix4.h" +#ifndef MAGNUM_TARGET_GLES2 +#include + +#include "Magnum/GL/Buffer.h" +#endif + #include "Magnum/Shaders/Implementation/CreateCompatibilityShader.h" namespace Magnum { namespace Shaders { -template VertexColorGL::VertexColorGL() { +namespace { + #ifndef MAGNUM_TARGET_GLES2 + enum: Int { + /* Not using the zero binding to avoid conflicts with + ProjectionBufferBinding from other shaders which can likely stay + bound to the same buffer for the whole time */ + TransformationProjectionBufferBinding = 1 + }; + #endif +} + +template VertexColorGL::VertexColorGL(const Flags flags + #ifndef MAGNUM_TARGET_GLES2 + , const UnsignedInt drawCount + #endif +): + _flags{flags} + #ifndef MAGNUM_TARGET_GLES2 + , _drawCount{drawCount} + #endif +{ + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || drawCount, + "Shaders::VertexColorGL: draw count can't be zero", ); + #endif + + #ifndef MAGNUM_TARGET_GLES + if(flags >= Flag::UniformBuffers) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); + #endif + #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ if(!Utility::Resource::hasGroup("MagnumShadersGL")) @@ -58,8 +95,16 @@ template VertexColorGL::VertexColorGL() { GL::Shader vert = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Vertex); GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment); - vert.addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n" : "#define THREE_DIMENSIONS\n") - .addSource(rs.get("generic.glsl")) + vert.addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n" : "#define THREE_DIMENSIONS\n"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + vert.addSource(Utility::formatString( + "#define UNIFORM_BUFFERS\n" + "#define DRAW_COUNT {}\n", + drawCount)); + } + #endif + vert.addSource(rs.get("generic.glsl")) .addSource(rs.get("VertexColor.vert")); frag.addSource(rs.get("generic.glsl")) .addSource(rs.get("VertexColor.frag")); @@ -85,21 +130,106 @@ template VertexColorGL::VertexColorGL() { if(!context.isExtensionSupported(version)) #endif { - _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + _drawOffsetUniform = uniformLocation("drawOffset"); + } else + #endif + { + _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); + } + } + + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers + #ifndef MAGNUM_TARGET_GLES + && !context.isExtensionSupported(version) + #endif + ) { + setUniformBlockBinding(uniformBlockIndex("TransformationProjection"), TransformationProjectionBufferBinding); } + #endif /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ #ifdef MAGNUM_TARGET_GLES - setTransformationProjectionMatrix(MatrixTypeFor{Math::IdentityInit}); + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::UniformBuffers) { + /* Draw offset is zero by default */ + } else + #endif + { + setTransformationProjectionMatrix(MatrixTypeFor{Math::IdentityInit}); + } #endif } +#ifndef MAGNUM_TARGET_GLES2 +template VertexColorGL::VertexColorGL(const Flags flags): VertexColorGL{flags, 1} {} +#endif + template VertexColorGL& VertexColorGL::setTransformationProjectionMatrix(const MatrixTypeFor& matrix) { + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), + "Shaders::VertexColorGL::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled", *this); + #endif setUniform(_transformationProjectionMatrixUniform, matrix); return *this; } +#ifndef MAGNUM_TARGET_GLES2 +template VertexColorGL& VertexColorGL::setDrawOffset(const UnsignedInt offset) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::VertexColorGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); + CORRADE_ASSERT(offset < _drawCount, + "Shaders::VertexColorGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); + setUniform(_drawOffsetUniform, offset); + return *this; +} + +template VertexColorGL& VertexColorGL::bindTransformationProjectionBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::VertexColorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); + return *this; +} + +template VertexColorGL& VertexColorGL::bindTransformationProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::VertexColorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); + return *this; +} +#endif + template class MAGNUM_SHADERS_EXPORT VertexColorGL<2>; template class MAGNUM_SHADERS_EXPORT VertexColorGL<3>; +namespace Implementation { + +Debug& operator<<(Debug& debug, const VertexColorGLFlag value) { + debug << "Shaders::VertexColorGL::Flag" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(v) case VertexColorGLFlag::v: return debug << "::" #v; + #ifndef MAGNUM_TARGET_GLES2 + _c(UniformBuffers) + #endif + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; +} + +Debug& operator<<(Debug& debug, const VertexColorGLFlags value) { + return Containers::enumSetDebugOutput(debug, value, "Shaders::VertexColorGL::Flags{}", { + #ifndef MAGNUM_TARGET_GLES2 + VertexColorGLFlag::UniformBuffers + #endif + }); +} + +} + }} diff --git a/src/Magnum/Shaders/VertexColorGL.h b/src/Magnum/Shaders/VertexColorGL.h index 513312fba..d84acec62 100644 --- a/src/Magnum/Shaders/VertexColorGL.h +++ b/src/Magnum/Shaders/VertexColorGL.h @@ -37,6 +37,15 @@ namespace Magnum { namespace Shaders { +namespace Implementation { + enum class VertexColorGLFlag: UnsignedByte { + #ifndef MAGNUM_TARGET_GLES2 + UniformBuffers = 1 << 0 + #endif + }; + typedef Containers::EnumSet VertexColorGLFlags; +} + /** @brief Vertex color OpenGL shader @m_since_latest @@ -107,7 +116,84 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ ColorOutput = GenericGL::ColorOutput }; - explicit VertexColorGL(); + #ifdef DOXYGEN_GENERATING_OUTPUT + /** + * @brief Flag + * @m_since{2020,06} + * + * @see @ref Flags, @ref flags() + */ + enum class Flag: UnsignedByte { + #ifndef MAGNUM_TARGET_GLES2 + /** + * Use uniform buffers. Expects that uniform data are supplied via + * @ref bindTransformationProjectionBuffer() instead of direct + * uniform setters. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES + * 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL + * 1.0. + * @m_since_latest + */ + UniformBuffers = 1 << 0 + #endif + }; + + /** + * @brief Flags + * @m_since{2020,06} + * + * @see @ref flags() + */ + typedef Containers::EnumSet Flags; + #else + /* Done this way to be prepared for possible future diversion of 2D + and 3D flags (e.g. introducing 3D-specific features) */ + typedef Implementation::VertexColorGLFlag Flag; + typedef Implementation::VertexColorGLFlags Flags; + #endif + + /** + * @brief Constructor + * @param flags Flags + * + * While this function is meant mainly for the classic uniform + * scenario (without @ref Flag::UniformBuffers set), it's equivalent to + * @ref VertexColorGL(Flags, UnsignedInt) with @p drawCount set to + * @cpp 1 @ce. + */ + explicit VertexColorGL(Flags flags = {}); + + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Construct for a multi-draw scenario + * @param flags Flags + * @param drawCount Size of a @ref TransformationProjectionUniform2D + * / @ref TransformationProjectionUniform3D buffer bound with + * @ref bindTransformationProjectionBuffer() + * + * If @p flags contains @ref Flag::UniformBuffers, @p drawCount + * describes the uniform buffer sizes as these are required to have a + * statically defined size. The draw offset is then set via + * @ref setDrawOffset(). + * + * If @p flags don't contain @ref Flag::UniformBuffers, @p drawCount is + * ignored and the constructor behaves the same as + * @ref VertexColorGL(Flags). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + /** @todo this constructor will eventually need to have also joint + count, per-vertex weight count, view count for multiview and clip + plane count ... and putting them in arbitrary order next to each + other is too error-prone, so it needs some other solution + (accepting pairs of parameter type and value like in GL context + creation, e.g., which will probably need a new enum as reusing Flag + for this might be too confusing) */ + explicit VertexColorGL(Flags flags, UnsignedInt drawCount); + #endif /** * @brief Construct without creating the underlying OpenGL object @@ -135,8 +221,28 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ /** @brief Move assignment */ VertexColorGL& operator=(VertexColorGL&&) noexcept = default; + /** @brief Flags */ + Flags flags() const { return _flags; } + + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Draw count + * @m_since_latest + * + * Statically defined size of each of the + * @ref TransformationProjectionUniform2D / + * @ref TransformationProjectionUniform3D uniform buffers. Has use only + * if @ref Flag::UniformBuffers is set. + * @requires_gles30 Not defined on OpenGL ES 2.0 builds. + * @requires_webgl20 Not defined on WebGL 1.0 builds. + */ + UnsignedInt drawCount() const { return _drawCount; } + #endif + /** @{ * @name Uniform setters + * + * Used only if @ref Flag::UniformBuffers is not set. */ /** @@ -144,6 +250,11 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ * @return Reference to self (for method chaining) * * Default is an identity matrix. + * + * Expects that @ref Flag::UniformBuffers is not set, in that case fill + * @ref TransformationProjectionUniform2D::transformationProjectionMatrix / + * @ref TransformationProjectionUniform3D::transformationProjectionMatrix + * and call @ref bindTransformationProjectionBuffer() instead. */ VertexColorGL& setTransformationProjectionMatrix(const MatrixTypeFor& matrix); @@ -151,6 +262,54 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ * @} */ + #ifndef MAGNUM_TARGET_GLES2 + /** @{ + * @name Uniform buffer binding and related uniform setters + * + * Used if @ref Flag::UniformBuffers is set. + */ + + /** + * @brief Set a draw offset + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Specifies which item in the @ref TransformationProjectionUniform2D / + * @ref TransformationProjectionUniform3D buffers bound with + * @ref bindTransformationProjectionBuffer() should be used for current + * draw. Expects that @ref Flag::UniformBuffers is set and @p offset is + * less than @ref drawCount(). Initial value is @cpp 0 @ce. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + VertexColorGL& setDrawOffset(UnsignedInt offset); + + /** + * @brief Set a transformation and projection uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref drawCount() instances of + * @ref TransformationProjectionUniform2D / + * @ref TransformationProjectionUniform3D. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + VertexColorGL& bindTransformationProjectionBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + VertexColorGL& bindTransformationProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + + /** + * @} + */ + #endif + private: /* Prevent accidentally calling irrelevant functions */ #ifndef MAGNUM_TARGET_GLES @@ -160,7 +319,16 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ using GL::AbstractShaderProgram::dispatchCompute; #endif + Flags _flags; + #ifndef MAGNUM_TARGET_GLES2 + UnsignedInt _drawCount{}; + #endif Int _transformationProjectionMatrixUniform{0}; + #ifndef MAGNUM_TARGET_GLES2 + /* Used instead of all other uniforms when Flag::UniformBuffers is set, + so it can alias them */ + Int _drawOffsetUniform{0}; + #endif }; /** @@ -175,6 +343,20 @@ typedef VertexColorGL<2> VertexColorGL2D; */ typedef VertexColorGL<3> VertexColorGL3D; +#ifdef DOXYGEN_GENERATING_OUTPUT +/** @debugoperatorclassenum{VertexColorGL,VertexColorGL::Flag} */ +template Debug& operator<<(Debug& debug, VertexColorGL::Flag value); + +/** @debugoperatorclassenum{VertexColorGL,VertexColorGL::Flags} */ +template Debug& operator<<(Debug& debug, VertexColorGL::Flags value); +#else +namespace Implementation { + MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, VertexColorGLFlag value); + MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, VertexColorGLFlags value); + CORRADE_ENUMSET_OPERATORS(VertexColorGLFlags) +} +#endif + }} #endif From 79d70f1e3aca1239af55438b1d943826fb36d42f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 24 May 2021 20:11:41 +0200 Subject: [PATCH 23/93] Shaders: add UBO variants to the benchmark. Interestingly, shaders that have indirect material references are about 2x slower on Intel. Not the Flat or Vector, which contain the full material in the DrawUniform. Will probably need extra Intel-specific optimizations (like avoiding the indirection if MATERIAL_COUNT=1). --- .../Shaders/Test/ShadersGLBenchmark.cpp | 403 +++++++++++++++--- 1 file changed, 343 insertions(+), 60 deletions(-) diff --git a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp index b92d3f93d..6a2d42ff4 100644 --- a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp +++ b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp @@ -54,6 +54,15 @@ #include "Magnum/Trade/AbstractImporter.h" #include "Magnum/Trade/MeshData.h" +#ifndef MAGNUM_TARGET_GLES2 +#include "Magnum/Shaders/DistanceFieldVector.h" +#include "Magnum/Shaders/Flat.h" +#include "Magnum/Shaders/Generic.h" +#include "Magnum/Shaders/MeshVisualizer.h" +#include "Magnum/Shaders/Phong.h" +#include "Magnum/Shaders/Vector.h" +#endif + #include "configure.h" namespace Magnum { namespace Shaders { namespace Test { namespace { @@ -121,99 +130,164 @@ constexpr std::size_t BenchmarkRepeats{4}; const struct { const char* name; FlatGL2D::Flags flags; + UnsignedInt drawCount; } FlatData[] { - {"", {}}, - {"vertex color", FlatGL2D::Flag::VertexColor}, + {"", {}, 1}, + {"vertex color", FlatGL2D::Flag::VertexColor, 1}, + #ifndef MAGNUM_TARGET_GLES2 + {"object ID", FlatGL2D::Flag::ObjectId, 1}, + #endif + {"textured", FlatGL2D::Flag::Textured, 1}, + {"textured + alpha mask", FlatGL2D::Flag::Textured|FlatGL2D::Flag::AlphaMask, 1}, + {"texture transformation", FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1}, + {"instanced transformation", FlatGL2D::Flag::InstancedTransformation, 1}, + {"instanced transformation + color", FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::VertexColor, 1}, #ifndef MAGNUM_TARGET_GLES2 - {"object ID", FlatGL2D::Flag::ObjectId}, + {"instanced transformation + object ID", FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::InstancedObjectId, 1}, #endif - {"textured", FlatGL2D::Flag::Textured}, - {"textured + alpha mask", FlatGL2D::Flag::Textured|FlatGL2D::Flag::AlphaMask}, - {"texture transformation", FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation}, - {"instanced transformation", FlatGL2D::Flag::InstancedTransformation}, - {"instanced transformation + color", FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::VertexColor}, + {"instanced transformation + texture offset", FlatGL2D::Flag::Textured|FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::InstancedTextureOffset, 1}, #ifndef MAGNUM_TARGET_GLES2 - {"instanced transformation + object ID", FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::InstancedObjectId}, + {"UBO single", FlatGL2D::Flag::UniformBuffers, 1}, + {"UBO single, texture transformation", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1}, + {"UBO multi", FlatGL2D::Flag::UniformBuffers, 128}, #endif - {"instanced transformation + texture offset", FlatGL2D::Flag::Textured|FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::InstancedTextureOffset}, }; const struct { const char* name; PhongGL::Flags flags; - UnsignedInt lights; + UnsignedInt lightCount, materialCount, drawCount; } PhongData[] { - {"", {}, 1}, - {"zero lights", {}, 0}, - {"five lights", {}, 5}, - {"vertex color", PhongGL::Flag::VertexColor, 1}, + {"", {}, 1, 1, 1}, + {"zero lights", {}, 0, 1, 1}, + {"five lights", {}, 5, 1, 1}, + {"vertex color", PhongGL::Flag::VertexColor, 1, 1, 1}, + #ifndef MAGNUM_TARGET_GLES2 + {"object ID", PhongGL::Flag::ObjectId, 1, 1, 1}, + #endif + {"diffuse texture", PhongGL::Flag::DiffuseTexture, 1, 1, 1}, + {"ADS textures", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture, 1, 1, 1}, + {"ADS textures + alpha mask", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::AlphaMask, 1, 1, 1}, + {"ADS textures + transformation", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureTransformation, 1, 1, 1}, + {"normal texture", PhongGL::Flag::NormalTexture, 1, 1, 1}, + {"normal texture with separate bitangent", PhongGL::Flag::NormalTexture|PhongGL::Flag::Bitangent, 1, 1, 1}, + {"instanced transformation", PhongGL::Flag::InstancedTransformation, 1, 1, 1}, + {"instanced transformation + color", PhongGL::Flag::InstancedTransformation|PhongGL::Flag::VertexColor, 1, 1, 1}, #ifndef MAGNUM_TARGET_GLES2 - {"object ID", PhongGL::Flag::ObjectId, 1}, - #endif - {"diffuse texture", PhongGL::Flag::DiffuseTexture, 1}, - {"ADS textures", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture, 1}, - {"ADS textures + alpha mask", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::AlphaMask, 1}, - {"ADS textures + transformation", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureTransformation, 1}, - {"normal texture", PhongGL::Flag::NormalTexture, 1}, - {"normal texture with separate bitangent", PhongGL::Flag::NormalTexture|PhongGL::Flag::Bitangent, 1}, - {"instanced transformation", PhongGL::Flag::InstancedTransformation, 1}, - {"instanced transformation + color", PhongGL::Flag::InstancedTransformation|PhongGL::Flag::VertexColor, 1}, + {"instanced transformation + object ID", PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedObjectId, 1, 1, 1}, + #endif + {"instanced transformation + ADS texture offset", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedTextureOffset, 1, 1, 1}, + #ifndef MAGNUM_TARGET_GLES2 + {"UBO single", PhongGL::Flag::UniformBuffers, 1, 1, 1}, + {"UBO single, zero lights", PhongGL::Flag::UniformBuffers, 0, 1, 1}, + {"UBO single five lights", PhongGL::Flag::UniformBuffers, 5, 1, 1}, + {"UBO single, ADS textures + transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureTransformation, 1, 1, 1}, + {"UBO multi, one light", PhongGL::Flag::UniformBuffers, 1, 32, 128}, + #endif +}; + +const struct { + const char* name; + VertexColorGL2D::Flags flags; + UnsignedInt drawCount; +} VertexColorData[] { + {"", {}, 1}, #ifndef MAGNUM_TARGET_GLES2 - {"instanced transformation + object ID", PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedObjectId, 1}, + {"UBO single", VertexColorGL2D::Flag::UniformBuffers, 1}, + {"UBO multi", VertexColorGL2D::Flag::UniformBuffers, 128}, #endif - {"instanced transformation + ADS texture offset", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedTextureOffset, 1}, }; const struct { const char* name; VectorGL2D::Flags flags; + UnsignedInt drawCount; } VectorData[] { - {"", {}}, - {"texture transformation", VectorGL2D::Flag::TextureTransformation} + {"", {}, 1}, + {"texture transformation", VectorGL2D::Flag::TextureTransformation, 1}, + #ifndef MAGNUM_TARGET_GLES2 + {"UBO single", VectorGL2D::Flag::UniformBuffers, 1}, + {"UBO single, texture transformation", VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, 1}, + {"UBO multi", VectorGL2D::Flag::UniformBuffers, 128}, + {"UBO multi, texture transformation", VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, 128}, + #endif }; const struct { const char* name; DistanceFieldVectorGL2D::Flags flags; + UnsignedInt materialCount, drawCount; } DistanceFieldVectorData[] { - {"", {}}, - {"texture transformation", DistanceFieldVectorGL2D::Flag::TextureTransformation} + {"", {}, 1, 1}, + {"texture transformation", DistanceFieldVectorGL2D::Flag::TextureTransformation, 1, 1}, + #ifndef MAGNUM_TARGET_GLES2 + {"UBO single", DistanceFieldVectorGL2D::Flag::UniformBuffers, 1, 1}, + {"UBO single, texture transformation", DistanceFieldVectorGL2D::Flag::UniformBuffers|DistanceFieldVectorGL2D::Flag::TextureTransformation, 1, 1}, + {"UBO multi", DistanceFieldVectorGL2D::Flag::UniformBuffers, 32, 128}, + {"UBO multi, texture transformation", DistanceFieldVectorGL2D::Flag::UniformBuffers|DistanceFieldVectorGL2D::Flag::TextureTransformation, 32, 128}, + #endif }; const struct { const char* name; MeshVisualizerGL2D::Flags flags; + UnsignedInt materialCount, drawCount; } MeshVisualizer2DData[] { #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - {"wireframe", MeshVisualizerGL2D::Flag::Wireframe}, + {"wireframe", MeshVisualizerGL2D::Flag::Wireframe, 1, 1}, #endif - {"wireframe w/o a GS", MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader}, + {"wireframe w/o a GS", MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 1, 1}, #ifndef MAGNUM_TARGET_GLES2 - {"instanced object ID", MeshVisualizerGL2D::Flag::InstancedObjectId}, - {"vertex ID", MeshVisualizerGL2D::Flag::VertexId}, + {"instanced object ID", MeshVisualizerGL2D::Flag::InstancedObjectId, 1, 1}, + {"vertex ID", MeshVisualizerGL2D::Flag::VertexId, 1, 1}, + #ifndef MAGNUM_TARGET_WEBGL + {"primitive ID", MeshVisualizerGL2D::Flag::PrimitiveId, 1, 1}, + {"primitive ID from vertex ID", MeshVisualizerGL2D::Flag::PrimitiveIdFromVertexId, 1, 1}, + #endif + #endif + #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + {"UBO single, wireframe", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe, 1, 1}, + #endif + {"UBO single, wireframe w/o a GS", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 1, 1}, + {"UBO single, vertex ID", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::VertexId, 1, 1}, #ifndef MAGNUM_TARGET_WEBGL - {"primitive ID", MeshVisualizerGL2D::Flag::PrimitiveId}, - {"primitive ID from vertex ID", MeshVisualizerGL2D::Flag::PrimitiveIdFromVertexId}, + {"UBO multi, wireframe", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe, 32, 128}, #endif + {"UBO multi, wireframe w/o a GS", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 32, 128}, + {"UBO multi, vertex ID", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::VertexId, 32, 128}, #endif }; const struct { const char* name; MeshVisualizerGL3D::Flags flags; + UnsignedInt materialCount, drawCount; } MeshVisualizer3DData[] { #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - {"wireframe", MeshVisualizerGL3D::Flag::Wireframe}, + {"wireframe", MeshVisualizerGL3D::Flag::Wireframe, 1, 1}, #endif - {"wireframe w/o a GS", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader}, + {"wireframe w/o a GS", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 1, 1}, #ifndef MAGNUM_TARGET_GLES2 - {"instanced object ID", MeshVisualizerGL3D::Flag::InstancedObjectId}, - {"vertex ID", MeshVisualizerGL3D::Flag::VertexId}, + {"instanced object ID", MeshVisualizerGL3D::Flag::InstancedObjectId, 1, 1}, + {"vertex ID", MeshVisualizerGL3D::Flag::VertexId, 1, 1}, #ifndef MAGNUM_TARGET_WEBGL - {"primitive ID", MeshVisualizerGL3D::Flag::PrimitiveId}, - {"primitive ID from vertex ID", MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId}, + {"primitive ID", MeshVisualizerGL3D::Flag::PrimitiveId, 1, 1}, + {"primitive ID from vertex ID", MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId, 1, 1}, #endif #endif + #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_WEBGL + {"UBO single, wireframe", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe, 1, 1}, + #endif + {"UBO single, wireframe w/o a GS", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 1, 1}, + {"UBO single, vertex ID", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::VertexId, 1, 1}, + #ifndef MAGNUM_TARGET_WEBGL + {"UBO multi, wireframe", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe, 32, 128}, + #endif + {"UBO multi, wireframe w/o a GS", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 32, 128}, + {"UBO multi, vertex ID", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::VertexId, 32, 128}, + #endif }; ShadersGLBenchmark::ShadersGLBenchmark(): _framebuffer{{{}, RenderSize}} { @@ -230,9 +304,9 @@ ShadersGLBenchmark::ShadersGLBenchmark(): _framebuffer{{{}, RenderSize}} { &ShadersGLBenchmark::renderTeardown, BenchmarkType::GpuTime); - addBenchmarks({&ShadersGLBenchmark::vertexColor<2>, + addInstancedBenchmarks({&ShadersGLBenchmark::vertexColor<2>, &ShadersGLBenchmark::vertexColor<3>}, - BenchmarkRepeats, + BenchmarkRepeats, Containers::arraySize(VertexColorData), &ShadersGLBenchmark::renderSetup, &ShadersGLBenchmark::renderTeardown, BenchmarkType::GpuTime); @@ -411,6 +485,16 @@ void ShadersGLBenchmark::renderTeardown() { /* Nothing to do here */ } +#ifndef MAGNUM_TARGET_GLES2 +template struct UniformTraits; +template<> struct UniformTraits<2> { + typedef TransformationProjectionUniform2D TransformationProjection; +}; +template<> struct UniformTraits<3> { + typedef TransformationProjectionUniform3D TransformationProjection; +}; +#endif + template void ShadersGLBenchmark::flat() { auto&& data = FlatData[testCaseInstanceId()]; setTestCaseTemplateName(Utility::formatString("{}", dimensions)); @@ -420,9 +504,38 @@ template void ShadersGLBenchmark::flat() { !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); - FlatGL shader{data.flags}; - if(data.flags >= FlatGL2D::Flag::AlphaMask) - shader.setAlphaMask(0.0f); + #ifndef MAGNUM_TARGET_GLES + if((data.flags & FlatGL2D::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + FlatGL shader{data.flags + #ifndef MAGNUM_TARGET_GLES2 + , data.drawCount + #endif + }; + + #ifndef MAGNUM_TARGET_GLES2 + GL::Buffer transformationProjectionUniform{NoCreate}; + GL::Buffer drawUniform{NoCreate}; + GL::Buffer textureTransformationUniform{NoCreate}; + if(data.flags & FlatGL2D::Flag::UniformBuffers) { + transformationProjectionUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array::TransformationProjection>{data.drawCount}}; + Containers::Array drawData{data.drawCount}; + drawData[0].setAlphaMask(0.0f); + drawUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, drawData}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform); + if(data.flags & FlatGL2D::Flag::TextureTransformation) { + textureTransformationUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; + shader.bindTextureTransformationBuffer(textureTransformationUniform); + } + } else + #endif + { + if(data.flags >= FlatGL2D::Flag::AlphaMask) + shader.setAlphaMask(0.0f); + } if(data.flags >= FlatGL2D::Flag::Textured) shader.bindTexture(_textureWhite); @@ -476,11 +589,54 @@ void ShadersGLBenchmark::phong() { !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); - PhongGL shader{data.flags, data.lights}; - /* White ambient so we always have a white output */ - shader.setAmbientColor(0xffffffff_rgbaf); - if(data.flags >= PhongGL::Flag::AlphaMask) - shader.setAlphaMask(0.0f); + #ifndef MAGNUM_TARGET_GLES + if((data.flags & PhongGL::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + PhongGL shader{data.flags, data.lightCount + #ifndef MAGNUM_TARGET_GLES2 + , data.materialCount, data.drawCount + #endif + }; + + #ifndef MAGNUM_TARGET_GLES2 + GL::Buffer projectionUniform{NoCreate}; + GL::Buffer transformationUniform{NoCreate}; + GL::Buffer drawUniform{NoCreate}; + GL::Buffer materialUniform{NoCreate}; + GL::Buffer lightUniform{NoCreate}; + GL::Buffer textureTransformationUniform{NoCreate}; + if(data.flags & PhongGL::Flag::UniformBuffers) { + projectionUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{} + }}; + transformationUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; + drawUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; + Containers::Array materialData{data.materialCount}; + materialData[0] + /* White ambient so we always have a white output */ + .setAmbientColor(0xffffffff_rgbaf) + .setAlphaMask(0.0f); + materialUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, materialData}; + lightUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.lightCount}}; + shader.bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .bindLightBuffer(lightUniform); + if(data.flags & PhongGL::Flag::TextureTransformation) { + textureTransformationUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; + shader.bindTextureTransformationBuffer(textureTransformationUniform); + } + } else + #endif + { + /* White ambient so we always have a white output */ + shader.setAmbientColor(0xffffffff_rgbaf); + if(data.flags >= PhongGL::Flag::AlphaMask) + shader.setAlphaMask(0.0f); + } if(data.flags >= PhongGL::Flag::AmbientTexture) shader.bindAmbientTexture(_textureWhite); if(data.flags >= PhongGL::Flag::DiffuseTexture) @@ -533,13 +689,33 @@ void ShadersGLBenchmark::phong() { } template void ShadersGLBenchmark::vertexColor() { + auto&& data = VertexColorData[testCaseInstanceId()]; setTestCaseTemplateName(Utility::formatString("{}", dimensions)); + setTestCaseDescription(data.name); if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); - VertexColorGL shader; + #ifndef MAGNUM_TARGET_GLES + if((data.flags & VertexColorGL2D::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + VertexColorGL shader{data.flags + #ifndef MAGNUM_TARGET_GLES2 + , data.drawCount + #endif + }; + + #ifndef MAGNUM_TARGET_GLES2 + GL::Buffer transformationProjectionUniform{NoCreate}; + GL::Buffer textureTransformationUniform{NoCreate}; + if(data.flags & VertexColorGL::Flag::UniformBuffers) { + transformationProjectionUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array::TransformationProjection>{data.drawCount}}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform); + } + #endif /* Warmup run */ /** @todo make this possible to do inside CORRADE_BENCHMARK() */ @@ -565,9 +741,36 @@ template void ShadersGLBenchmark::vector() { !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); - VectorGL shader{data.flags}; + #ifndef MAGNUM_TARGET_GLES + if((data.flags & VectorGL2D::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + VectorGL shader{data.flags + #ifndef MAGNUM_TARGET_GLES2 + , data.drawCount + #endif + }; shader.bindVectorTexture(_textureWhite); + #ifndef MAGNUM_TARGET_GLES2 + GL::Buffer transformationProjectionUniform{NoCreate}; + GL::Buffer drawUniform{NoCreate}; + GL::Buffer textureTransformationUniform{NoCreate}; + if(data.flags & VectorGL2D::Flag::UniformBuffers) { + transformationProjectionUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array::TransformationProjection>{data.drawCount}}; + drawUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform); + if(data.flags & VectorGL2D::Flag::TextureTransformation) { + textureTransformationUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + }}; + shader.bindTextureTransformationBuffer(textureTransformationUniform); + } + } + #endif + /* Warmup run */ /** @todo make this possible to do inside CORRADE_BENCHMARK() */ for(std::size_t i = 0; i != WarmupIterations; ++i) @@ -592,9 +795,37 @@ template void ShadersGLBenchmark::distanceFieldVector() !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); - DistanceFieldVectorGL shader{data.flags}; + #ifndef MAGNUM_TARGET_GLES + if((data.flags & DistanceFieldVectorGL2D::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + DistanceFieldVectorGL shader{data.flags + #ifndef MAGNUM_TARGET_GLES2 + , data.materialCount, data.drawCount + #endif + }; shader.bindVectorTexture(_textureWhite); + #ifndef MAGNUM_TARGET_GLES2 + GL::Buffer transformationProjectionUniform{NoCreate}; + GL::Buffer drawUniform{NoCreate}; + GL::Buffer materialUniform{NoCreate}; + GL::Buffer textureTransformationUniform{NoCreate}; + if(data.flags & DistanceFieldVectorGL2D::Flag::UniformBuffers) { + transformationProjectionUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array::TransformationProjection>{data.drawCount}}; + drawUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; + materialUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.materialCount}}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform); + if(data.flags & DistanceFieldVectorGL2D::Flag::TextureTransformation) { + textureTransformationUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; + shader.bindTextureTransformationBuffer(textureTransformationUniform); + } + } + #endif + /* Warmup run */ /** @todo make this possible to do inside CORRADE_BENCHMARK() */ for(std::size_t i = 0; i != WarmupIterations; ++i) @@ -618,6 +849,11 @@ void ShadersGLBenchmark::meshVisualizer2D() { !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + #ifndef MAGNUM_TARGET_GLES + if((data.flags & MeshVisualizerGL2D::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + /* Checks verbatim copied from MeshVisualizerGLTest::construct2D() */ #ifndef MAGNUM_TARGET_GLES if((data.flags & MeshVisualizerGL2D::Flag::InstancedObjectId) && !GL::Context::current().isExtensionSupported()) @@ -663,8 +899,6 @@ void ShadersGLBenchmark::meshVisualizer2D() { MeshVisualizerGL2D shader{data.flags}; shader.setViewportSize(Vector2{RenderSize}); - if(data.flags >= MeshVisualizerGL2D::Flag::Wireframe) - shader.setWireframeColor(0xffffffff_rgbaf); #ifndef MAGNUM_TARGET_GLES2 if(data.flags & (MeshVisualizerGL2D::Flag::InstancedObjectId|MeshVisualizerGL2D::Flag::VertexId|MeshVisualizerGL2D::Flag::PrimitiveIdFromVertexId #ifndef MAGNUM_TARGET_WEBGL @@ -674,6 +908,26 @@ void ShadersGLBenchmark::meshVisualizer2D() { shader.bindColorMapTexture(_textureWhite); #endif + #ifndef MAGNUM_TARGET_GLES2 + GL::Buffer transformationProjectionUniform{NoCreate}; + GL::Buffer drawUniform{NoCreate}; + GL::Buffer materialUniform{NoCreate}; + if(data.flags & MeshVisualizerGL2D::Flag::UniformBuffers) { + transformationProjectionUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; + drawUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; + Containers::Array materialData{data.materialCount}; + materialData[0].setWireframeColor(0xffffffff_rgbaf); + materialUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, materialData}; + shader.bindTransformationProjectionBuffer(transformationProjectionUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform); + } else + #endif + { + if(data.flags >= MeshVisualizerGL2D::Flag::Wireframe) + shader.setWireframeColor(0xffffffff_rgbaf); + } + GL::Mesh* mesh; if(data.flags >= MeshVisualizerGL2D::Flag::NoGeometryShader) { mesh = &_meshDuplicated; @@ -714,6 +968,11 @@ void ShadersGLBenchmark::meshVisualizer3D() { !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + #ifndef MAGNUM_TARGET_GLES + if((data.flags & MeshVisualizerGL3D::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + /* Checks verbatim copied from MeshVisualizerGLTest:.construct3D() */ #ifndef MAGNUM_TARGET_GLES if((data.flags & MeshVisualizerGL3D::Flag::InstancedObjectId) && !GL::Context::current().isExtensionSupported()) @@ -759,8 +1018,7 @@ void ShadersGLBenchmark::meshVisualizer3D() { MeshVisualizerGL3D shader{data.flags}; shader.setViewportSize(Vector2{RenderSize}); - if(data.flags >= MeshVisualizerGL3D::Flag::Wireframe) - shader.setWireframeColor(0xffffffff_rgbaf); + #ifndef MAGNUM_TARGET_GLES2 if(data.flags & (MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::VertexId|MeshVisualizerGL3D::Flag::PrimitiveIdFromVertexId #ifndef MAGNUM_TARGET_WEBGL @@ -770,6 +1028,31 @@ void ShadersGLBenchmark::meshVisualizer3D() { shader.bindColorMapTexture(_textureWhite); #endif + #ifndef MAGNUM_TARGET_GLES2 + GL::Buffer projectionUniform{NoCreate}; + GL::Buffer transformationUniform{NoCreate}; + GL::Buffer drawUniform{NoCreate}; + GL::Buffer materialUniform{NoCreate}; + if(data.flags & MeshVisualizerGL3D::Flag::UniformBuffers) { + projectionUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{} + }}; + transformationUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; + drawUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; + Containers::Array materialData{data.materialCount}; + materialData[0].setWireframeColor(0xffffffff_rgbaf); + materialUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, materialData}; + shader.bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform); + } else + #endif + { + if(data.flags >= MeshVisualizerGL3D::Flag::Wireframe) + shader.setWireframeColor(0xffffffff_rgbaf); + } + GL::Mesh* mesh; if(data.flags >= MeshVisualizerGL3D::Flag::NoGeometryShader) mesh = &_meshDuplicated; From 0ea727f21e4e0c874bd6fde6f6dee91ff49ec703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 20 May 2021 16:58:06 +0200 Subject: [PATCH 24/93] Shaders: ah, GCC, and your optimizations. --- .../Shaders/Test/DistanceFieldVectorTest.cpp | 16 ++++++-- src/Magnum/Shaders/Test/FlatTest.cpp | 9 ++++- src/Magnum/Shaders/Test/GenericTest.cpp | 37 ++++++++++++++++--- .../Shaders/Test/MeshVisualizerTest.cpp | 25 ++++++++++--- src/Magnum/Shaders/Test/PhongTest.cpp | 31 ++++++++++++---- src/Magnum/Shaders/Test/VectorTest.cpp | 9 ++++- 6 files changed, 101 insertions(+), 26 deletions(-) diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorTest.cpp index bdb22c184..5ec7a4c8f 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorTest.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorTest.cpp @@ -105,7 +105,12 @@ void DistanceFieldVectorTest::drawUniformConstructNoInit() { a.materialId = 76; new(&a) DistanceFieldVectorDrawUniform{NoInit}; - CORRADE_COMPARE(a.materialId, 76); + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.materialId, 76); + } CORRADE_VERIFY(std::is_nothrow_constructible::value); @@ -168,8 +173,13 @@ void DistanceFieldVectorTest::materialUniformConstructNoInit() { a.outlineEnd = 0.37f; new(&a) DistanceFieldVectorMaterialUniform{NoInit}; - CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); - CORRADE_COMPARE(a.outlineEnd, 0.37f); + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); + CORRADE_COMPARE(a.outlineEnd, 0.37f); + } CORRADE_VERIFY(std::is_nothrow_constructible::value); diff --git a/src/Magnum/Shaders/Test/FlatTest.cpp b/src/Magnum/Shaders/Test/FlatTest.cpp index 8365429ca..1a5a86df3 100644 --- a/src/Magnum/Shaders/Test/FlatTest.cpp +++ b/src/Magnum/Shaders/Test/FlatTest.cpp @@ -100,8 +100,13 @@ void FlatTest::drawUniformConstructNoInit() { a.alphaMask = 0.7f; new(&a) FlatDrawUniform{NoInit}; - CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); - CORRADE_COMPARE(a.alphaMask, 0.7f); + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); + CORRADE_COMPARE(a.alphaMask, 0.7f); + } CORRADE_VERIFY(std::is_nothrow_constructible::value); diff --git a/src/Magnum/Shaders/Test/GenericTest.cpp b/src/Magnum/Shaders/Test/GenericTest.cpp index 744cfaae1..f6a03fcea 100644 --- a/src/Magnum/Shaders/Test/GenericTest.cpp +++ b/src/Magnum/Shaders/Test/GenericTest.cpp @@ -154,7 +154,12 @@ void GenericTest::projectionUniform2DConstructNoInit() { a.projectionMatrix[2] = {1.5f, 0.3f, 3.1f, 0.5f}; new(&a) ProjectionUniform2D{NoInit}; - CORRADE_COMPARE(a.projectionMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.projectionMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); + } CORRADE_VERIFY(std::is_nothrow_constructible::value); @@ -215,7 +220,12 @@ void GenericTest::projectionUniform3DConstructNoInit() { a.projectionMatrix[2] = {1.5f, 0.3f, 3.1f, 0.5f}; new(&a) ProjectionUniform3D{NoInit}; - CORRADE_COMPARE(a.projectionMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.projectionMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); + } CORRADE_VERIFY(std::is_nothrow_constructible::value); @@ -268,7 +278,12 @@ void GenericTest::transformationUniform2DConstructNoInit() { a.transformationMatrix[2] = {1.5f, 0.3f, 3.1f, 0.5f}; new(&a) TransformationUniform2D{NoInit}; - CORRADE_COMPARE(a.transformationMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.transformationMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); + } CORRADE_VERIFY(std::is_nothrow_constructible::value); @@ -329,7 +344,12 @@ void GenericTest::transformationUniform3DConstructNoInit() { a.transformationMatrix[2] = {1.5f, 0.3f, 3.1f, 0.5f}; new(&a) TransformationUniform3D{NoInit}; - CORRADE_COMPARE(a.transformationMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.transformationMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); + } CORRADE_VERIFY(std::is_nothrow_constructible::value); @@ -383,8 +403,13 @@ void GenericTest::textureTransformationUniformConstructNoInit() { a.offset = {2.7f, 0.3f}; new(&a) TextureTransformationUniform{NoInit}; - CORRADE_COMPARE(a.rotationScaling[1], (Vector2{2.5f, -3.0f})); - CORRADE_COMPARE(a.offset, (Vector2{2.7f, 0.3f})); + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.rotationScaling[1], (Vector2{2.5f, -3.0f})); + CORRADE_COMPARE(a.offset, (Vector2{2.7f, 0.3f})); + } CORRADE_VERIFY(std::is_nothrow_constructible::value); diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerTest.cpp index e9bad7799..e6abe8c6a 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerTest.cpp @@ -120,7 +120,12 @@ void MeshVisualizerTest::drawUniform2DConstructNoInit() { a.materialId = 73; new(&a) MeshVisualizerDrawUniform2D{NoInit}; - CORRADE_COMPARE(a.materialId, 73); + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.materialId, 73); + } CORRADE_VERIFY(std::is_nothrow_constructible::value); @@ -187,8 +192,13 @@ void MeshVisualizerTest::drawUniform3DConstructNoInit() { a.materialId = 5; new(&a) MeshVisualizerDrawUniform3D{NoInit}; - CORRADE_COMPARE(a.normalMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); - CORRADE_COMPARE(a.materialId, 5); + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.normalMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); + CORRADE_COMPARE(a.materialId, 5); + } CORRADE_VERIFY(std::is_nothrow_constructible::value); @@ -269,8 +279,13 @@ void MeshVisualizerTest::materialUniformConstructNoInit() { a.lineWidth = 0.765f; new(&a) MeshVisualizerMaterialUniform{NoInit}; - CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); - CORRADE_COMPARE(a.lineWidth, 0.765f); + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); + CORRADE_COMPARE(a.lineWidth, 0.765f); + } CORRADE_VERIFY(std::is_nothrow_constructible::value); diff --git a/src/Magnum/Shaders/Test/PhongTest.cpp b/src/Magnum/Shaders/Test/PhongTest.cpp index 7276b6440..e29330d3b 100644 --- a/src/Magnum/Shaders/Test/PhongTest.cpp +++ b/src/Magnum/Shaders/Test/PhongTest.cpp @@ -152,9 +152,14 @@ void PhongTest::drawUniformConstructNoInit() { a.lightCount = 7; new(&a) PhongDrawUniform{NoInit}; - CORRADE_COMPARE(a.normalMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); - CORRADE_COMPARE(a.materialId, 5); - CORRADE_COMPARE(a.lightCount, 7); + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.normalMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f})); + CORRADE_COMPARE(a.materialId, 5); + CORRADE_COMPARE(a.lightCount, 7); + } CORRADE_VERIFY(std::is_nothrow_constructible::value); @@ -233,9 +238,14 @@ void PhongTest::materialUniformConstructNoInit() { a.alphaMask = 7.0f; new(&a) PhongMaterialUniform{NoInit}; - CORRADE_COMPARE(a.diffuseColor, 0x354565fc_rgbaf); - CORRADE_COMPARE(a.normalTextureScale, 0.4f); - CORRADE_COMPARE(a.alphaMask, 7.0f); + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.diffuseColor, 0x354565fc_rgbaf); + CORRADE_COMPARE(a.normalTextureScale, 0.4f); + CORRADE_COMPARE(a.alphaMask, 7.0f); + } CORRADE_VERIFY(std::is_nothrow_constructible::value); @@ -296,8 +306,13 @@ void PhongTest::lightUniformConstructNoInit() { a.range = 7.0f; new(&a) PhongLightUniform{NoInit}; - CORRADE_COMPARE(a.color, 0x354565_rgbf); - CORRADE_COMPARE(a.range, 7.0f); + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.color, 0x354565_rgbf); + CORRADE_COMPARE(a.range, 7.0f); + } CORRADE_VERIFY(std::is_nothrow_constructible::value); diff --git a/src/Magnum/Shaders/Test/VectorTest.cpp b/src/Magnum/Shaders/Test/VectorTest.cpp index ce3be11bf..b3e49cd2b 100644 --- a/src/Magnum/Shaders/Test/VectorTest.cpp +++ b/src/Magnum/Shaders/Test/VectorTest.cpp @@ -96,8 +96,13 @@ void VectorTest::drawUniformConstructNoInit() { a.backgroundColor = 0x98769facb_rgbaf; new(&a) VectorDrawUniform{NoInit}; - CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); - CORRADE_COMPARE(a.backgroundColor, 0x98769facb_rgbaf); + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); + CORRADE_COMPARE(a.backgroundColor, 0x98769facb_rgbaf); + } CORRADE_VERIFY(std::is_nothrow_constructible::value); From 0863d9e811fb56e421ecaf796264e08f5b68d975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 20 May 2021 16:59:18 +0200 Subject: [PATCH 25/93] Shaders: reassign MeshVisualizer uniforms to be contiguous for UBOs too. --- src/Magnum/Shaders/MeshVisualizer.frag | 4 ++-- src/Magnum/Shaders/MeshVisualizer.geom | 4 ++-- src/Magnum/Shaders/MeshVisualizer.vert | 8 ++++---- src/Magnum/Shaders/MeshVisualizerGL.h | 18 +++++++++--------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Magnum/Shaders/MeshVisualizer.frag b/src/Magnum/Shaders/MeshVisualizer.frag index e75b64eea..c88460c0f 100644 --- a/src/Magnum/Shaders/MeshVisualizer.frag +++ b/src/Magnum/Shaders/MeshVisualizer.frag @@ -100,7 +100,7 @@ uniform lowp float smoothness #if defined(INSTANCED_OBJECT_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID) #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 6) +layout(location = 5) #endif uniform lowp vec2 colorMapOffsetScale #ifndef GL_ES @@ -115,7 +115,7 @@ uniform lowp vec2 colorMapOffsetScale #else #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 0) +layout(location = 1) #endif uniform highp uint drawOffset #ifndef GL_ES diff --git a/src/Magnum/Shaders/MeshVisualizer.geom b/src/Magnum/Shaders/MeshVisualizer.geom index 928e1f2b2..89709595f 100644 --- a/src/Magnum/Shaders/MeshVisualizer.geom +++ b/src/Magnum/Shaders/MeshVisualizer.geom @@ -39,7 +39,7 @@ /* This one is for both classic and UBOs, as it's usually set globally instead of changing per-draw */ #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 5) +layout(location = 0) #endif uniform lowp vec2 viewportSize; /* defaults to zero */ @@ -88,7 +88,7 @@ uniform lowp float smoothness #else #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 0) +layout(location = 1) #endif uniform highp uint drawOffset #ifndef GL_ES diff --git a/src/Magnum/Shaders/MeshVisualizer.vert b/src/Magnum/Shaders/MeshVisualizer.vert index 44b270b50..38ce85ffb 100644 --- a/src/Magnum/Shaders/MeshVisualizer.vert +++ b/src/Magnum/Shaders/MeshVisualizer.vert @@ -41,7 +41,7 @@ #ifndef UNIFORM_BUFFERS #ifdef TWO_DIMENSIONS #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 0) +layout(location = 6) #endif uniform highp mat3 transformationProjectionMatrix #ifndef GL_ES @@ -50,7 +50,7 @@ uniform highp mat3 transformationProjectionMatrix ; #elif defined(THREE_DIMENSIONS) #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 0) +layout(location = 6) #endif uniform highp mat4 transformationMatrix #ifndef GL_ES @@ -71,7 +71,7 @@ uniform highp mat4 projectionMatrix #ifdef VERTEX_ID #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 6) +layout(location = 5) #endif uniform lowp vec2 colorMapOffsetScale #ifndef GL_ES @@ -106,7 +106,7 @@ uniform highp float lineLength #else #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 0) +layout(location = 1) #endif uniform highp uint drawOffset #ifndef GL_ES diff --git a/src/Magnum/Shaders/MeshVisualizerGL.h b/src/Magnum/Shaders/MeshVisualizerGL.h index ca21d8de9..fa750c895 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.h +++ b/src/Magnum/Shaders/MeshVisualizerGL.h @@ -97,16 +97,16 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgr #ifndef MAGNUM_TARGET_GLES2 UnsignedInt _materialCount{}, _drawCount{}; #endif - Int _colorUniform{1}, + Int _viewportSizeUniform{0}, + _colorUniform{1}, _wireframeColorUniform{2}, _wireframeWidthUniform{3}, - _smoothnessUniform{4}, - _viewportSizeUniform{5}; + _smoothnessUniform{4}; #ifndef MAGNUM_TARGET_GLES2 - Int _colorMapOffsetScaleUniform{6}; - /* Used instead of all other uniforms when Flag::UniformBuffers is set, - so it can alias them */ - Int _drawOffsetUniform{0}; + Int _colorMapOffsetScaleUniform{5}; + /* Used instead of all other uniforms except viewportSize when + Flag::UniformBuffers is set, so it can alias them */ + Int _drawOffsetUniform{1}; #endif }; @@ -565,7 +565,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua */ private: - Int _transformationProjectionMatrixUniform{0}; + Int _transformationProjectionMatrixUniform{6}; }; /** @@ -1514,7 +1514,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua */ private: - Int _transformationMatrixUniform{0}, + Int _transformationMatrixUniform{6}, _projectionMatrixUniform{7}; #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) Int _normalMatrixUniform{8}, From 0512edf75489031b7008174ee951248264739c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 21 May 2021 12:43:11 +0200 Subject: [PATCH 26/93] Shaders: Clang 3.8, WHY are you runing my nice tidy paddings. --- src/Magnum/Shaders/DistanceFieldVector.h | 84 +++++++++++++++--- src/Magnum/Shaders/Flat.h | 41 +++++++-- src/Magnum/Shaders/Generic.h | 19 +++- src/Magnum/Shaders/MeshVisualizer.h | 105 +++++++++++++++++++---- src/Magnum/Shaders/Phong.h | 78 ++++++++++++++--- src/Magnum/Shaders/Vector.h | 49 +++++++++-- 6 files changed, 321 insertions(+), 55 deletions(-) diff --git a/src/Magnum/Shaders/DistanceFieldVector.h b/src/Magnum/Shaders/DistanceFieldVector.h index dd5d665a8..a6c452aa1 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.h +++ b/src/Magnum/Shaders/DistanceFieldVector.h @@ -55,7 +55,18 @@ be shared among multiple draw calls and thus are provided in a separate */ struct DistanceFieldVectorDrawUniform { /** @brief Construct with default parameters */ - constexpr explicit DistanceFieldVectorDrawUniform(DefaultInitT = DefaultInit) noexcept: materialId{0} {} + constexpr explicit DistanceFieldVectorDrawUniform(DefaultInitT = DefaultInit) noexcept: + #if ((defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8)) && defined(CORRADE_TARGET_BIG_ENDIAN) + _pad0{}, /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + materialId{0} + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + #ifndef CORRADE_TARGET_BIG_ENDIAN + , _pad0{} + #endif + , _pad1{}, _pad2{}, _pad3{} + #endif + {} /** @brief Construct without initializing the contents */ explicit DistanceFieldVectorDrawUniform(NoInitT) noexcept {} @@ -101,19 +112,39 @@ struct DistanceFieldVectorDrawUniform { /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; /* reserved for skinOffset */ #endif #else - UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; /* reserved for skinOffset */ UnsignedShort materialId; #endif /* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - Int:32; /* reserved for objectId */ - Int:32; - Int:32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; /* reserved for objectId */ + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad2 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad3 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; #endif }; @@ -127,7 +158,16 @@ Describes material properties referenced from */ struct DistanceFieldVectorMaterialUniform { /** @brief Construct with default parameters */ - constexpr explicit DistanceFieldVectorMaterialUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f}, outlineColor{0.0f, 0.0f, 0.0f, 0.0f}, outlineStart{0.5f}, outlineEnd{1.0f}, smoothness{0.04f} {} + constexpr explicit DistanceFieldVectorMaterialUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f}, + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + /* Otherwise it refuses to constexpr, on 3.8 at least */ + _pad0{}, _pad1{}, _pad2{}, _pad3{}, + #endif + outlineColor{0.0f, 0.0f, 0.0f, 0.0f}, outlineStart{0.5f}, outlineEnd{1.0f}, smoothness{0.04f} + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + , _pad4{} + #endif + {} /** @brief Construct without initializing the contents */ explicit DistanceFieldVectorMaterialUniform(NoInitT) noexcept: color{NoInit}, outlineColor{NoInit} {} @@ -193,10 +233,26 @@ struct DistanceFieldVectorMaterialUniform { /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - Int:32; /* reserved for backgroundColor */ - Int:32; - Int:32; - Int:32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; /* reserved for backgroundColor */ + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad2 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad3 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; #endif /** @@ -241,7 +297,11 @@ struct DistanceFieldVectorMaterialUniform { /* warning: Member __pad4__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - Int:32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad4 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; #endif }; diff --git a/src/Magnum/Shaders/Flat.h b/src/Magnum/Shaders/Flat.h index f11ea91e1..e5bef400b 100644 --- a/src/Magnum/Shaders/Flat.h +++ b/src/Magnum/Shaders/Flat.h @@ -52,7 +52,16 @@ each draw call. Texture transformation, if needed, is supplied separately in a */ struct FlatDrawUniform { /** @brief Construct with default parameters */ - constexpr explicit FlatDrawUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f}, objectId{0}, alphaMask{0.5f} {} + constexpr explicit FlatDrawUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f}, objectId{0}, + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + /* Otherwise it refuses to constexpr, on 3.8 at least */ + _pad0{}, _pad1{}, + #endif + alphaMask{0.5f} + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + , _pad2{} + #endif + {} /** @brief Construct without initializing the contents */ explicit FlatDrawUniform(NoInitT) noexcept: color{NoInit} {} @@ -128,11 +137,27 @@ struct FlatDrawUniform { /* This field is an UnsignedInt in the shader and skinOffset is extracted as (value >> 16), so the order has to be different on BE */ #ifndef CORRADE_TARGET_BIG_ENDIAN - UnsignedShort:16; - UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; /* reserved for skinOffset */ #else - UnsignedShort:16; /* reserved for skinOffset */ - UnsignedShort:16; + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; /* reserved for skinOffset */ + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; #endif #endif @@ -150,7 +175,11 @@ struct FlatDrawUniform { /* warning: Member __pad2__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - Int:32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad2 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; #endif }; diff --git a/src/Magnum/Shaders/Generic.h b/src/Magnum/Shaders/Generic.h index c7f73a340..9322b1054 100644 --- a/src/Magnum/Shaders/Generic.h +++ b/src/Magnum/Shaders/Generic.h @@ -374,7 +374,12 @@ Used only if @ref DistanceFieldVectorGL::Flag::TextureTransformation, */ struct TextureTransformationUniform { /** @brief Construct with default parameters */ - constexpr explicit TextureTransformationUniform(DefaultInitT = DefaultInit) noexcept: rotationScaling{Math::IdentityInit} {} + constexpr explicit TextureTransformationUniform(DefaultInitT = DefaultInit) noexcept: rotationScaling{Math::IdentityInit} + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + /* Otherwise it refuses to constexpr, on 3.8 at least */ + , _pad0{}, _pad1{} + #endif + {} /** @brief Construct without initializing the contents */ explicit TextureTransformationUniform(NoInitT) noexcept: rotationScaling{NoInit}, offset{NoInit} {} @@ -447,8 +452,16 @@ struct TextureTransformationUniform { /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - Int:32; /* reserved for layer */ - Int:32; /* reserved for coordinateSet */ + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; /* reserved for layer */ + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; /* reserved for coordinateSet */ #endif }; diff --git a/src/Magnum/Shaders/MeshVisualizer.h b/src/Magnum/Shaders/MeshVisualizer.h index 85463592c..2441daae1 100644 --- a/src/Magnum/Shaders/MeshVisualizer.h +++ b/src/Magnum/Shaders/MeshVisualizer.h @@ -54,7 +54,18 @@ separate @ref MeshVisualizerMaterialUniform structure, referenced by */ struct MeshVisualizerDrawUniform2D { /** @brief Construct with default parameters */ - constexpr explicit MeshVisualizerDrawUniform2D(DefaultInitT = DefaultInit) noexcept: materialId{0} {} + constexpr explicit MeshVisualizerDrawUniform2D(DefaultInitT = DefaultInit) noexcept: + #if ((defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8)) && defined(CORRADE_TARGET_BIG_ENDIAN) + _pad0{}, /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + materialId{0} + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + #ifndef CORRADE_TARGET_BIG_ENDIAN + , _pad0{} + #endif + , _pad1{}, _pad2{}, _pad3{} + #endif + {} /** @brief Construct without initializing the contents */ explicit MeshVisualizerDrawUniform2D(NoInitT) noexcept {} @@ -100,19 +111,39 @@ struct MeshVisualizerDrawUniform2D { /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; /* reserved for skinOffset */ #endif #else - UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; /* reserved for skinOffset */ UnsignedShort materialId; #endif /* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - Int:32; - Int:32; - Int:32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad2 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad3 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; #endif }; @@ -128,7 +159,18 @@ shared among multiple draw calls and thus are provided in a separate */ struct MeshVisualizerDrawUniform3D { /** @brief Construct with default parameters */ - constexpr explicit MeshVisualizerDrawUniform3D(DefaultInitT = DefaultInit) noexcept: normalMatrix{Math::IdentityInit}, materialId{0} {} + constexpr explicit MeshVisualizerDrawUniform3D(DefaultInitT = DefaultInit) noexcept: normalMatrix{Math::IdentityInit} + #if ((defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8)) && defined(CORRADE_TARGET_BIG_ENDIAN) + , _pad0{} /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + , materialId{0} + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + #ifndef CORRADE_TARGET_BIG_ENDIAN + , _pad0{} + #endif + , _pad1{}, _pad2{}, _pad3{} + #endif + {} /** @brief Construct without initializing the contents */ explicit MeshVisualizerDrawUniform3D(NoInitT) noexcept: normalMatrix{NoInit} {} @@ -194,19 +236,39 @@ struct MeshVisualizerDrawUniform3D { /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; /* reserved for skinOffset */ #endif #else - UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; /* reserved for skinOffset */ UnsignedShort materialId; #endif /* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - Int:32; - Int:32; - Int:32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad2 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad3 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; #endif }; @@ -220,7 +282,12 @@ Describes material properties referenced from */ struct MeshVisualizerMaterialUniform { /** @brief Construct with default parameters */ - constexpr explicit MeshVisualizerMaterialUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f}, wireframeColor{0.0f, 0.0f, 0.0f, 1.0f}, wireframeWidth{1.0f}, colorMapOffset{1.0f/512.0f}, colorMapScale{1.0f/256.0f}, lineWidth{1.0f}, lineLength{1.0f}, smoothness{2.0f} {} + constexpr explicit MeshVisualizerMaterialUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f}, wireframeColor{0.0f, 0.0f, 0.0f, 1.0f}, wireframeWidth{1.0f}, colorMapOffset{1.0f/512.0f}, colorMapScale{1.0f/256.0f}, lineWidth{1.0f}, lineLength{1.0f}, smoothness{2.0f} + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + /* Otherwise it refuses to constexpr, on 3.8 at least */ + , _pad0{}, _pad1{} + #endif + {} /** @brief Construct without initializing the contents */ explicit MeshVisualizerMaterialUniform(NoInitT) noexcept: color{NoInit}, wireframeColor{NoInit} {} @@ -432,8 +499,16 @@ struct MeshVisualizerMaterialUniform { /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - Int:32; - Int:32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; #endif }; diff --git a/src/Magnum/Shaders/Phong.h b/src/Magnum/Shaders/Phong.h index 8252f62e9..c315e171d 100644 --- a/src/Magnum/Shaders/Phong.h +++ b/src/Magnum/Shaders/Phong.h @@ -55,7 +55,15 @@ separate @ref PhongMaterialUniform structure, referenced by @ref materialId. */ struct PhongDrawUniform { /** @brief Construct with default parameters */ - constexpr explicit PhongDrawUniform(DefaultInitT = DefaultInit) noexcept: normalMatrix{Math::IdentityInit}, materialId{0}, objectId{0}, lightOffset{0}, lightCount{0xffffffffu} {} + constexpr explicit PhongDrawUniform(DefaultInitT = DefaultInit) noexcept: normalMatrix{Math::IdentityInit}, + #if ((defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8)) && defined(CORRADE_TARGET_BIG_ENDIAN) + _pad0{}, /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + materialId{0}, + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) && !defined(CORRADE_TARGET_BIG_ENDIAN) + _pad0{}, + #endif + objectId{0}, lightOffset{0}, lightCount{0xffffffffu} {} /** @brief Construct without initializing the contents */ explicit PhongDrawUniform(NoInitT) noexcept: normalMatrix{NoInit} {} @@ -144,10 +152,18 @@ struct PhongDrawUniform { /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; /* reserved for skinOffset */ #endif #else - UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; /* reserved for skinOffset */ UnsignedShort materialId; #endif @@ -195,7 +211,11 @@ Describes material properties referenced from */ struct PhongMaterialUniform { /** @brief Construct with default parameters */ - constexpr explicit PhongMaterialUniform(DefaultInitT = DefaultInit) noexcept: ambientColor{0.0f, 0.0f, 0.0f, 0.0f}, diffuseColor{1.0f, 1.0f, 1.0f, 1.0f}, specularColor{1.0f, 1.0f, 1.0f, 0.0f}, normalTextureScale{1.0f}, shininess{80.0f}, alphaMask{0.5f} {} + constexpr explicit PhongMaterialUniform(DefaultInitT = DefaultInit) noexcept: ambientColor{0.0f, 0.0f, 0.0f, 0.0f}, diffuseColor{1.0f, 1.0f, 1.0f, 1.0f}, specularColor{1.0f, 1.0f, 1.0f, 0.0f}, normalTextureScale{1.0f}, shininess{80.0f}, alphaMask{0.5f} + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + , _pad0{} /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + {} /** @brief Construct without initializing the contents */ explicit PhongMaterialUniform(NoInitT) noexcept: ambientColor{NoInit}, diffuseColor{NoInit}, specularColor{NoInit} {} @@ -345,7 +365,11 @@ struct PhongMaterialUniform { /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - Int:32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; #endif }; @@ -359,7 +383,19 @@ and @ref PhongDrawUniform::lightCount range. */ struct PhongLightUniform { /** @brief Construct with default parameters */ - constexpr explicit PhongLightUniform(DefaultInitT = DefaultInit) noexcept: position{0.0f, 0.0f, 1.0f, 0.0f}, color{1.0f, 1.0f, 1.0f}, specularColor{1.0f, 1.0f, 1.0f}, range{Constants::inf()} {} + constexpr explicit PhongLightUniform(DefaultInitT = DefaultInit) noexcept: position{0.0f, 0.0f, 1.0f, 0.0f}, color{1.0f, 1.0f, 1.0f}, + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0{}, /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + specularColor{1.0f, 1.0f, 1.0f}, + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad1{}, + #endif + range{Constants::inf()} + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + , _pad2{}, _pad3{}, _pad4{} + #endif + {} /** @brief Construct without initializing the contents */ explicit PhongLightUniform(NoInitT) noexcept: position{NoInit}, color{NoInit}, specularColor{NoInit} {} @@ -436,7 +472,11 @@ struct PhongLightUniform { /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - Int:32; /* reserved for cone inner angle */ + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; /* reserved for cone inner angle */ #endif /** @@ -452,7 +492,11 @@ struct PhongLightUniform { /* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - Int:32; /* reserved for cone outer angle */ + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; /* reserved for cone outer angle */ #endif /** @@ -466,9 +510,21 @@ struct PhongLightUniform { /* warning: Member __pad2__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - Int:32; /* reserved for cone direction */ - Int:32; - Int:32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad2 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; /* reserved for cone direction */ + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad3 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad4 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; #endif }; diff --git a/src/Magnum/Shaders/Vector.h b/src/Magnum/Shaders/Vector.h index f110efbfb..f45ce9df5 100644 --- a/src/Magnum/Shaders/Vector.h +++ b/src/Magnum/Shaders/Vector.h @@ -52,7 +52,12 @@ each draw call. Texture transformation, if needed, is supplied separately in a */ struct VectorDrawUniform { /** @brief Construct with default parameters */ - constexpr explicit VectorDrawUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f}, backgroundColor{0.0f, 0.0f, 0.0f, 0.0f} {} + constexpr explicit VectorDrawUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f}, backgroundColor{0.0f, 0.0f, 0.0f, 0.0f} + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + /* Otherwise it refuses to constexpr, on 3.8 at least */ + , _pad0{}, _pad1{}, _pad2{}, _pad3{}, _pad4{} + #endif + {} /** @brief Construct without initializing the contents */ explicit VectorDrawUniform(NoInitT) noexcept: color{NoInit}, backgroundColor{NoInit} {} @@ -110,15 +115,43 @@ struct VectorDrawUniform { /* This field is an UnsignedInt in the shader and skinOffset is extracted as (value >> 16), so the order has to be different on BE */ #ifndef CORRADE_TARGET_BIG_ENDIAN - UnsignedShort:16; - UnsignedShort:16; /* reserved for skinOffset */ + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; /* reserved for skinOffset */ #else - UnsignedShort:16; /* reserved for skinOffset */ - UnsignedShort:16; + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; /* reserved for skinOffset */ + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; #endif - Int:32; /* reserved for objectId */ - Int:32; /* reserved for alphaMask */ - Int:32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad2 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; /* reserved for objectId */ + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad3 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; /* reserved for alphaMask */ + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad4 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; #endif }; From 4aa52179470907ae9ec26020dcbe967d40f95fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 21 May 2021 12:52:44 +0200 Subject: [PATCH 27/93] Shaders: a crutch for MSVC's neverending fails. Hope this is enough? --- .../Test/DistanceFieldVectorGLTest.cpp | 6 ++-- src/Magnum/Shaders/Test/FlatGLTest.cpp | 18 ++++++---- .../Shaders/Test/MeshVisualizerGLTest.cpp | 21 +++++++---- src/Magnum/Shaders/Test/PhongGLTest.cpp | 36 ++++++++++++------- src/Magnum/Shaders/Test/VectorGLTest.cpp | 6 ++-- src/Magnum/Shaders/Test/VertexColorGLTest.cpp | 3 +- 6 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp index fd92030f2..b75dd72eb 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp @@ -259,7 +259,8 @@ DistanceFieldVectorGLTest::DistanceFieldVectorGLTest() { #endif }); - addTests({ + /* MSVC needs explicit type due to default template args */ + addTests({ &DistanceFieldVectorGLTest::renderDefaults2D, #ifndef MAGNUM_TARGET_GLES2 &DistanceFieldVectorGLTest::renderDefaults2D, @@ -272,7 +273,8 @@ DistanceFieldVectorGLTest::DistanceFieldVectorGLTest() { &DistanceFieldVectorGLTest::renderSetup, &DistanceFieldVectorGLTest::renderTeardown); - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &DistanceFieldVectorGLTest::render2D, #ifndef MAGNUM_TARGET_GLES2 &DistanceFieldVectorGLTest::render2D, diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index 35882e81c..715026036 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -336,7 +336,8 @@ FlatGLTest::FlatGLTest() { #endif }); - addTests({ + /* MSVC needs explicit type due to default template args */ + addTests({ &FlatGLTest::renderDefaults2D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderDefaults2D, @@ -365,7 +366,8 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderSetup, &FlatGLTest::renderTeardown); - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &FlatGLTest::renderTextured2D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderTextured2D, @@ -379,7 +381,8 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderSetup, &FlatGLTest::renderTeardown); - addTests({ + /* MSVC needs explicit type due to default template args */ + addTests({ &FlatGLTest::renderVertexColor2D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderVertexColor2D, @@ -400,7 +403,8 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderSetup, &FlatGLTest::renderTeardown); - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &FlatGLTest::renderAlpha2D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderAlpha2D, @@ -415,7 +419,8 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderAlphaTeardown); #ifndef MAGNUM_TARGET_GLES2 - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &FlatGLTest::renderObjectId2D, &FlatGLTest::renderObjectId2D, &FlatGLTest::renderObjectId3D, @@ -425,7 +430,8 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderObjectIdTeardown); #endif - addTests({ + /* MSVC needs explicit type due to default template args */ + addTests({ &FlatGLTest::renderInstanced2D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderInstanced2D, diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index 717f11326..7caf1d2f4 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -675,7 +675,8 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { }); #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - addTests({ + /* MSVC needs explicit type due to default template args */ + addTests({ &MeshVisualizerGLTest::renderDefaultsWireframe2D, #ifndef MAGNUM_TARGET_GLES2 &MeshVisualizerGLTest::renderDefaultsWireframe2D, @@ -690,7 +691,8 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { #endif #ifndef MAGNUM_TARGET_GLES2 - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &MeshVisualizerGLTest::renderDefaultsObjectId2D, #ifndef MAGNUM_TARGET_GLES2 &MeshVisualizerGLTest::renderDefaultsObjectId2D, @@ -706,7 +708,8 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { #endif #ifndef MAGNUM_TARGET_GLES2 - addTests({ + /* MSVC needs explicit type due to default template args */ + addTests({ &MeshVisualizerGLTest::renderDefaultsVertexId2D, #ifndef MAGNUM_TARGET_GLES2 &MeshVisualizerGLTest::renderDefaultsVertexId2D, @@ -734,7 +737,8 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { &MeshVisualizerGLTest::renderTeardown); #endif - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &MeshVisualizerGLTest::renderWireframe2D, #ifndef MAGNUM_TARGET_GLES2 &MeshVisualizerGLTest::renderWireframe2D, @@ -744,7 +748,8 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &MeshVisualizerGLTest::renderWireframe3D, #ifndef MAGNUM_TARGET_GLES2 &MeshVisualizerGLTest::renderWireframe3D, @@ -755,7 +760,8 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { &MeshVisualizerGLTest::renderTeardown); #ifndef MAGNUM_TARGET_GLES2 - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &MeshVisualizerGLTest::renderObjectVertexPrimitiveId2D, #ifndef MAGNUM_TARGET_GLES2 &MeshVisualizerGLTest::renderObjectVertexPrimitiveId2D, @@ -775,7 +781,8 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() { &MeshVisualizerGLTest::renderSetup, &MeshVisualizerGLTest::renderTeardown); - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &MeshVisualizerGLTest::renderTangentBitangentNormal, #ifndef MAGNUM_TARGET_GLES2 &MeshVisualizerGLTest::renderTangentBitangentNormal, diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index fb8f96411..520bbb120 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -620,7 +620,8 @@ PhongGLTest::PhongGLTest() { #endif }); - addTests({ + /* MSVC needs explicit type due to default template args */ + addTests({ &PhongGLTest::renderDefaults, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderDefaults @@ -629,7 +630,8 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &PhongGLTest::renderColored, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderColored @@ -639,7 +641,8 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &PhongGLTest::renderSinglePixelTextured, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderSinglePixelTextured @@ -649,7 +652,8 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &PhongGLTest::renderTextured, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderTextured @@ -659,7 +663,8 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &PhongGLTest::renderTexturedNormal, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderTexturedNormal @@ -669,7 +674,8 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addTests({ + /* MSVC needs explicit type due to default template args */ + addTests({ &PhongGLTest::renderVertexColor, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderVertexColor, @@ -682,7 +688,8 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &PhongGLTest::renderShininess, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderShininess, @@ -692,7 +699,8 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &PhongGLTest::renderAlpha, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderAlpha @@ -703,7 +711,8 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderAlphaTeardown); #ifndef MAGNUM_TARGET_GLES2 - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &PhongGLTest::renderObjectId, &PhongGLTest::renderObjectId}, Containers::arraySize(RenderObjectIdData), @@ -711,7 +720,8 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderObjectIdTeardown); #endif - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &PhongGLTest::renderLights, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderLights, @@ -726,7 +736,8 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addTests({ + /* MSVC needs explicit type due to default template args */ + addTests({ &PhongGLTest::renderZeroLights, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderZeroLights @@ -741,7 +752,8 @@ PhongGLTest::PhongGLTest() { #endif ); - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &PhongGLTest::renderInstanced, #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::renderInstanced, diff --git a/src/Magnum/Shaders/Test/VectorGLTest.cpp b/src/Magnum/Shaders/Test/VectorGLTest.cpp index 0a25f7038..9474bec3e 100644 --- a/src/Magnum/Shaders/Test/VectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VectorGLTest.cpp @@ -237,7 +237,8 @@ VectorGLTest::VectorGLTest() { #endif }); - addTests({ + /* MSVC needs explicit type due to default template args */ + addTests({ &VectorGLTest::renderDefaults2D, #ifndef MAGNUM_TARGET_GLES2 &VectorGLTest::renderDefaults2D, @@ -250,7 +251,8 @@ VectorGLTest::VectorGLTest() { &VectorGLTest::renderSetup, &VectorGLTest::renderTeardown); - addInstancedTests({ + /* MSVC needs explicit type due to default template args */ + addInstancedTests({ &VectorGLTest::render2D, #ifndef MAGNUM_TARGET_GLES2 &VectorGLTest::render2D, diff --git a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp index 692883dd9..43fc382b6 100644 --- a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp @@ -188,7 +188,8 @@ VertexColorGLTest::VertexColorGLTest() { #endif }); - addTests({ + /* MSVC needs explicit type due to default template args */ + addTests({ &VertexColorGLTest::renderDefaults2D, #ifndef MAGNUM_TARGET_GLES2 &VertexColorGLTest::renderDefaults2D, From 24f6c33069fae29cda5ace127c4cca496eab59f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 21 May 2021 17:20:07 +0200 Subject: [PATCH 28/93] Shaders: ye, SwiftShader, thanks for reminding me of the harsh reality. 256 vectors in total is really damn low, guess I'll have to optimize the remaining uniform structures to deduplicate the redundant material info. --- src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp | 4 +++- src/Magnum/Shaders/Test/FlatGLTest.cpp | 4 +++- src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp | 8 ++++++-- src/Magnum/Shaders/Test/PhongGLTest.cpp | 6 ++++-- src/Magnum/Shaders/Test/VectorGLTest.cpp | 4 +++- src/Magnum/Shaders/Test/VertexColorGLTest.cpp | 4 +++- 6 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp index b75dd72eb..6319f8d4a 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp @@ -156,7 +156,9 @@ constexpr struct { {"classic fallback", {}, 1, 1}, {"", DistanceFieldVectorGL2D::Flag::UniformBuffers, 1, 1}, {"texture transformation", DistanceFieldVectorGL2D::Flag::UniformBuffers|DistanceFieldVectorGL2D::Flag::TextureTransformation, 1, 1}, - {"multiple materials, draws", DistanceFieldVectorGL2D::Flag::UniformBuffers, 64, 128}, + /* SwiftShader has 256 uniform vectors at most, per-draw is 4+1 in 3D case + and 3+1 in 2D, per-material 4 */ + {"multiple materials, draws", DistanceFieldVectorGL2D::Flag::UniformBuffers, 16, 48}, }; constexpr struct { diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index 715026036..39f2eeaa2 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -199,7 +199,9 @@ constexpr struct { } ConstructUniformBuffersData[]{ {"classic fallback", {}, 1}, {"", FlatGL2D::Flag::UniformBuffers, 1}, - {"multiple draws", FlatGL2D::Flag::UniformBuffers, 128}, + /* SwiftShader has 256 uniform vectors at most, per-draw is 4+2 in 3D case + and 3+2 in 2D */ + {"multiple draws", FlatGL2D::Flag::UniformBuffers, 42}, {"texture transformation", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1}, {"alpha mask", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::AlphaMask, 1}, {"object ID", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::ObjectId, 1} diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index 7caf1d2f4..3aec29ec1 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -215,7 +215,9 @@ constexpr struct { } ConstructUniformBuffersData2D[] { {"classic fallback", MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 1, 1}, {"", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 1, 1}, - {"multiple materials, draws", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 64, 128}, + /* SwiftShader has 256 uniform vectors at most, per-2D-draw is 4, + per-material 4, two need to be left for drawOffset + viewportSize */ + {"multiple materials, draws", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 8, 55}, /* The rest is basically a copy of ConstructData2D with UniformBuffers added */ #ifndef MAGNUM_TARGET_WEBGL @@ -271,7 +273,9 @@ constexpr struct { } ConstructUniformBuffersData3D[] { {"classic fallback", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 1, 1}, {"", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 1, 1}, - {"multiple materials, draws", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 64, 128}, + /* SwiftShader has 256 uniform vectors at most, per-3D-draw is 4+4, + per-material 4, plus 4 for projection */ + {"multiple materials, draws", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 6, 28}, /* The rest is basically a copy of ConstructData2D with UniformBuffers added */ #ifndef MAGNUM_TARGET_WEBGL diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 520bbb120..66ba51c8d 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -218,8 +218,10 @@ constexpr struct { } ConstructUniformBuffersData[]{ {"classic fallback", {}, 1, 1, 1}, {"", PhongGL::Flag::UniformBuffers, 1, 1, 1}, - {"multiple lights, materials, draws", PhongGL::Flag::UniformBuffers, 30, 64, 128}, - {"zero lights", PhongGL::Flag::UniformBuffers, 0, 64, 128}, + /* SwiftShader has 256 uniform vectors at most, per-3D-draw is 4+4, + per-material 4, per-light 4 plus 4 for projection */ + {"multiple lights, materials, draws", PhongGL::Flag::UniformBuffers, 8, 8, 24}, + {"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}, {"normal texture", PhongGL::Flag::UniformBuffers|PhongGL::Flag::NormalTexture, 1, 1, 1}, diff --git a/src/Magnum/Shaders/Test/VectorGLTest.cpp b/src/Magnum/Shaders/Test/VectorGLTest.cpp index 9474bec3e..0d492b8e1 100644 --- a/src/Magnum/Shaders/Test/VectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VectorGLTest.cpp @@ -155,7 +155,9 @@ constexpr struct { {"classic fallback", {}, 1}, {"", VectorGL2D::Flag::UniformBuffers, 1}, {"texture transformation", VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, 1}, - {"multiple draws", VectorGL2D::Flag::UniformBuffers, 128}, + /* SwiftShader has 256 uniform vectors at most, per-draw is 4+3 in 3D case + and 3+3 in 2D */ + {"multiple draws", VectorGL2D::Flag::UniformBuffers, 36}, }; #endif diff --git a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp index 43fc382b6..e9011fe62 100644 --- a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp @@ -132,7 +132,9 @@ constexpr struct { } ConstructUniformBuffersData[]{ {"classic fallback", {}, 1}, {"", VertexColorGL2D::Flag::UniformBuffers, 1}, - {"multiple draws", VertexColorGL2D::Flag::UniformBuffers, 128} + /* SwiftShader has 256 uniform vectors at most, per-draw is 4 in 3D case + and 3 in 2D; one needs to be reserved for drawOffset */ + {"multiple draws", VertexColorGL2D::Flag::UniformBuffers, 63} }; #endif From b62f1903ebfa838a67b33d30c72f374d9188c737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 21 May 2021 17:21:26 +0200 Subject: [PATCH 29/93] Shaders: ensure the structures are 4-byte aligned. --- src/Magnum/Shaders/DistanceFieldVector.h | 4 ++-- src/Magnum/Shaders/MeshVisualizer.h | 4 ++-- .../Shaders/Test/DistanceFieldVectorTest.cpp | 10 ++++++---- src/Magnum/Shaders/Test/FlatTest.cpp | 8 +++++--- src/Magnum/Shaders/Test/GenericTest.cpp | 16 +++++++++------- src/Magnum/Shaders/Test/MeshVisualizerTest.cpp | 12 +++++++----- src/Magnum/Shaders/Test/PhongTest.cpp | 12 +++++++----- src/Magnum/Shaders/Test/VectorTest.cpp | 8 +++++--- 8 files changed, 43 insertions(+), 31 deletions(-) diff --git a/src/Magnum/Shaders/DistanceFieldVector.h b/src/Magnum/Shaders/DistanceFieldVector.h index a6c452aa1..8964793d7 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.h +++ b/src/Magnum/Shaders/DistanceFieldVector.h @@ -108,7 +108,7 @@ struct DistanceFieldVectorDrawUniform { /* This field is an UnsignedInt in the shader and materialId is extracted as (value & 0xffff), so the order has to be different on BE */ #ifndef CORRADE_TARGET_BIG_ENDIAN - UnsignedShort materialId; + alignas(4) UnsignedShort materialId; /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT @@ -119,7 +119,7 @@ struct DistanceFieldVectorDrawUniform { :16; /* reserved for skinOffset */ #endif #else - UnsignedShort + alignas(4) UnsignedShort #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ #endif diff --git a/src/Magnum/Shaders/MeshVisualizer.h b/src/Magnum/Shaders/MeshVisualizer.h index 2441daae1..2e4038412 100644 --- a/src/Magnum/Shaders/MeshVisualizer.h +++ b/src/Magnum/Shaders/MeshVisualizer.h @@ -107,7 +107,7 @@ struct MeshVisualizerDrawUniform2D { /* This field is an UnsignedInt in the shader and materialId is extracted as (value & 0xffff), so the order has to be different on BE */ #ifndef CORRADE_TARGET_BIG_ENDIAN - UnsignedShort materialId; + alignas(4) UnsignedShort materialId; /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT @@ -118,7 +118,7 @@ struct MeshVisualizerDrawUniform2D { :16; /* reserved for skinOffset */ #endif #else - UnsignedShort + alignas(4) UnsignedShort #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ #endif diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorTest.cpp index 5ec7a4c8f..768591731 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorTest.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorTest.cpp @@ -33,7 +33,7 @@ namespace Magnum { namespace Shaders { namespace Test { namespace { struct DistanceFieldVectorTest: TestSuite::Tester { explicit DistanceFieldVectorTest(); - template void uniformSize(); + template void uniformSizeAlignment(); void drawUniformConstructDefault(); void drawUniformConstructNoInit(); @@ -46,8 +46,8 @@ struct DistanceFieldVectorTest: TestSuite::Tester { }; DistanceFieldVectorTest::DistanceFieldVectorTest() { - addTests({&DistanceFieldVectorTest::uniformSize, - &DistanceFieldVectorTest::uniformSize, + addTests({&DistanceFieldVectorTest::uniformSizeAlignment, + &DistanceFieldVectorTest::uniformSizeAlignment, &DistanceFieldVectorTest::drawUniformConstructDefault, &DistanceFieldVectorTest::drawUniformConstructNoInit, @@ -69,7 +69,7 @@ template<> struct UniformTraits { static const char* name() { return "DistanceFieldVectorMaterialUniform"; } }; -template void DistanceFieldVectorTest::uniformSize() { +template void DistanceFieldVectorTest::uniformSizeAlignment() { setTestCaseTemplateName(UniformTraits::name()); CORRADE_FAIL_IF(sizeof(T) % sizeof(Vector4) != 0, sizeof(T) << "is not a multiple of vec4 for UBO alignment."); @@ -79,6 +79,8 @@ template void DistanceFieldVectorTest::uniformSize() { CORRADE_FAIL_IF(768 % sizeof(T) != 0, sizeof(T) << "can't fit exactly into 768-byte UBO alignment."); if(256 % sizeof(T) != 0) CORRADE_WARN(sizeof(T) << "can't fit exactly into 256-byte UBO alignment, only 768."); + + CORRADE_COMPARE(alignof(T), 4); } void DistanceFieldVectorTest::drawUniformConstructDefault() { diff --git a/src/Magnum/Shaders/Test/FlatTest.cpp b/src/Magnum/Shaders/Test/FlatTest.cpp index 1a5a86df3..3ab604d65 100644 --- a/src/Magnum/Shaders/Test/FlatTest.cpp +++ b/src/Magnum/Shaders/Test/FlatTest.cpp @@ -33,7 +33,7 @@ namespace Magnum { namespace Shaders { namespace Test { namespace { struct FlatTest: TestSuite::Tester { explicit FlatTest(); - template void uniformSize(); + template void uniformSizeAlignment(); void drawUniformConstructDefault(); void drawUniformConstructNoInit(); @@ -41,7 +41,7 @@ struct FlatTest: TestSuite::Tester { }; FlatTest::FlatTest() { - addTests({&FlatTest::uniformSize, + addTests({&FlatTest::uniformSizeAlignment, &FlatTest::drawUniformConstructDefault, &FlatTest::drawUniformConstructNoInit, @@ -55,7 +55,7 @@ template<> struct UniformTraits { static const char* name() { return "FlatDrawUniform"; } }; -template void FlatTest::uniformSize() { +template void FlatTest::uniformSizeAlignment() { setTestCaseTemplateName(UniformTraits::name()); CORRADE_FAIL_IF(sizeof(T) % sizeof(Vector4) != 0, sizeof(T) << "is not a multiple of vec4 for UBO alignment."); @@ -65,6 +65,8 @@ template void FlatTest::uniformSize() { CORRADE_FAIL_IF(768 % sizeof(T) != 0, sizeof(T) << "can't fit exactly into 768-byte UBO alignment."); if(256 % sizeof(T) != 0) CORRADE_WARN(sizeof(T) << "can't fit exactly into 256-byte UBO alignment, only 768."); + + CORRADE_COMPARE(alignof(T), 4); } void FlatTest::drawUniformConstructDefault() { diff --git a/src/Magnum/Shaders/Test/GenericTest.cpp b/src/Magnum/Shaders/Test/GenericTest.cpp index f6a03fcea..0abd1809c 100644 --- a/src/Magnum/Shaders/Test/GenericTest.cpp +++ b/src/Magnum/Shaders/Test/GenericTest.cpp @@ -33,7 +33,7 @@ namespace Magnum { namespace Shaders { namespace Test { namespace { struct GenericTest: TestSuite::Tester { explicit GenericTest(); - template void uniformSize(); + template void uniformSizeAlignment(); void projectionUniform2DConstructDefault(); void projectionUniform2DConstructNoInit(); @@ -57,11 +57,11 @@ struct GenericTest: TestSuite::Tester { }; GenericTest::GenericTest() { - addTests({&GenericTest::uniformSize, - &GenericTest::uniformSize, - &GenericTest::uniformSize, - &GenericTest::uniformSize, - &GenericTest::uniformSize, + addTests({&GenericTest::uniformSizeAlignment, + &GenericTest::uniformSizeAlignment, + &GenericTest::uniformSizeAlignment, + &GenericTest::uniformSizeAlignment, + &GenericTest::uniformSizeAlignment, &GenericTest::projectionUniform2DConstructDefault, &GenericTest::projectionUniform2DConstructNoInit, @@ -103,7 +103,7 @@ template<> struct UniformTraits { static const char* name() { return "TextureTransformationUniform"; } }; -template void GenericTest::uniformSize() { +template void GenericTest::uniformSizeAlignment() { setTestCaseTemplateName(UniformTraits::name()); CORRADE_FAIL_IF(sizeof(T) % sizeof(Vector4) != 0, sizeof(T) << "is not a multiple of vec4 for UBO alignment."); @@ -113,6 +113,8 @@ template void GenericTest::uniformSize() { CORRADE_FAIL_IF(768 % sizeof(T) != 0, sizeof(T) << "can't fit exactly into 768-byte UBO alignment."); if(256 % sizeof(T) != 0) CORRADE_WARN(sizeof(T) << "can't fit exactly into 256-byte UBO alignment, only 768."); + + CORRADE_COMPARE(alignof(T), 4); } void GenericTest::projectionUniform2DConstructDefault() { diff --git a/src/Magnum/Shaders/Test/MeshVisualizerTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerTest.cpp index e6abe8c6a..178c261e5 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerTest.cpp @@ -34,7 +34,7 @@ namespace Magnum { namespace Shaders { namespace Test { namespace { struct MeshVisualizerTest: TestSuite::Tester { explicit MeshVisualizerTest(); - template void uniformSize(); + template void uniformSizeAlignment(); void drawUniform2DConstructDefault(); void drawUniform2DConstructNoInit(); @@ -52,9 +52,9 @@ struct MeshVisualizerTest: TestSuite::Tester { }; MeshVisualizerTest::MeshVisualizerTest() { - addTests({&MeshVisualizerTest::uniformSize, - &MeshVisualizerTest::uniformSize, - &MeshVisualizerTest::uniformSize, + addTests({&MeshVisualizerTest::uniformSizeAlignment, + &MeshVisualizerTest::uniformSizeAlignment, + &MeshVisualizerTest::uniformSizeAlignment, &MeshVisualizerTest::drawUniform2DConstructDefault, &MeshVisualizerTest::drawUniform2DConstructNoInit, @@ -84,7 +84,7 @@ template<> struct UniformTraits { static const char* name() { return "MeshVisualizerMaterialUniform"; } }; -template void MeshVisualizerTest::uniformSize() { +template void MeshVisualizerTest::uniformSizeAlignment() { setTestCaseTemplateName(UniformTraits::name()); CORRADE_FAIL_IF(sizeof(T) % sizeof(Vector4) != 0, sizeof(T) << "is not a multiple of vec4 for UBO alignment"); @@ -94,6 +94,8 @@ template void MeshVisualizerTest::uniformSize() { CORRADE_FAIL_IF(768 % sizeof(T) != 0, sizeof(T) << "can't fit exactly into 768-byte UBO alignment"); if(256 % sizeof(T) != 0) CORRADE_WARN(sizeof(T) << "can't fit exactly into 256-byte UBO alignment, only 768"); + + CORRADE_COMPARE(alignof(T), 4); } void MeshVisualizerTest::drawUniform2DConstructDefault() { diff --git a/src/Magnum/Shaders/Test/PhongTest.cpp b/src/Magnum/Shaders/Test/PhongTest.cpp index e29330d3b..510da0a51 100644 --- a/src/Magnum/Shaders/Test/PhongTest.cpp +++ b/src/Magnum/Shaders/Test/PhongTest.cpp @@ -34,7 +34,7 @@ namespace Magnum { namespace Shaders { namespace Test { namespace { struct PhongTest: TestSuite::Tester { explicit PhongTest(); - template void uniformSize(); + template void uniformSizeAlignment(); void drawUniformConstructDefault(); void drawUniformConstructNoInit(); @@ -51,9 +51,9 @@ struct PhongTest: TestSuite::Tester { }; PhongTest::PhongTest() { - addTests({&PhongTest::uniformSize, - &PhongTest::uniformSize, - &PhongTest::uniformSize, + addTests({&PhongTest::uniformSizeAlignment, + &PhongTest::uniformSizeAlignment, + &PhongTest::uniformSizeAlignment, &PhongTest::drawUniformConstructDefault, &PhongTest::drawUniformConstructNoInit, @@ -82,7 +82,7 @@ template<> struct UniformTraits { static const char* name() { return "PhongLightUniform"; } }; -template void PhongTest::uniformSize() { +template void PhongTest::uniformSizeAlignment() { setTestCaseTemplateName(UniformTraits::name()); CORRADE_FAIL_IF(sizeof(T) % sizeof(Vector4) != 0, sizeof(T) << "is not a multiple of vec4 for UBO alignment."); @@ -92,6 +92,8 @@ template void PhongTest::uniformSize() { CORRADE_FAIL_IF(768 % sizeof(T) != 0, sizeof(T) << "can't fit exactly into 768-byte UBO alignment."); if(256 % sizeof(T) != 0) CORRADE_WARN(sizeof(T) << "can't fit exactly into 256-byte UBO alignment, only 768."); + + CORRADE_COMPARE(alignof(T), 4); } void PhongTest::drawUniformConstructDefault() { diff --git a/src/Magnum/Shaders/Test/VectorTest.cpp b/src/Magnum/Shaders/Test/VectorTest.cpp index b3e49cd2b..fffb94115 100644 --- a/src/Magnum/Shaders/Test/VectorTest.cpp +++ b/src/Magnum/Shaders/Test/VectorTest.cpp @@ -33,7 +33,7 @@ namespace Magnum { namespace Shaders { namespace Test { namespace { struct VectorTest: TestSuite::Tester { explicit VectorTest(); - template void uniformSize(); + template void uniformSizeAlignment(); void drawUniformConstructDefault(); void drawUniformConstructNoInit(); @@ -41,7 +41,7 @@ struct VectorTest: TestSuite::Tester { }; VectorTest::VectorTest() { - addTests({&VectorTest::uniformSize, + addTests({&VectorTest::uniformSizeAlignment, &VectorTest::drawUniformConstructDefault, &VectorTest::drawUniformConstructNoInit, @@ -55,7 +55,7 @@ template<> struct UniformTraits { static const char* name() { return "VectorDrawUniform"; } }; -template void VectorTest::uniformSize() { +template void VectorTest::uniformSizeAlignment() { setTestCaseTemplateName(UniformTraits::name()); CORRADE_FAIL_IF(sizeof(T) % sizeof(Vector4) != 0, sizeof(T) << "is not a multiple of vec4 for UBO alignment."); @@ -65,6 +65,8 @@ template void VectorTest::uniformSize() { CORRADE_FAIL_IF(768 % sizeof(T) != 0, sizeof(T) << "can't fit exactly into 768-byte UBO alignment."); if(256 % sizeof(T) != 0) CORRADE_WARN(sizeof(T) << "can't fit exactly into 256-byte UBO alignment, only 768."); + + CORRADE_COMPARE(alignof(T), 4); } void VectorTest::drawUniformConstructDefault() { From 2ca8d0ebb2c8cd8ffeca680bbce93d25e939cc66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 23 May 2021 16:01:05 +0200 Subject: [PATCH 30/93] Shaders: clarify what the heck. --- src/Magnum/Shaders/Test/PhongGLTest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 66ba51c8d..6c141d5fd 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -2794,6 +2794,7 @@ template void PhongGLTest::renderInstanced() { Matrix3x3 normal; Color3 color; Vector2 textureOffset; + /* instanced ObjectId tested directly in renderObjectId() */ } instanceData[] { {Matrix4::translation({-1.25f, -1.25f, 0.0f})* Matrix4::rotationX(90.0_degf), From b109216caa01d3d94549fa2d840be2337b8da4eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 24 May 2021 23:03:24 +0200 Subject: [PATCH 31/93] Shaders: what in the name of fuck. That feeling when you lose three hours debugging STRANGE shader compiler issues that happen only on ES, seeing stuff like "unexpected HASH_TOKEN at line 140" or "unterminated ifdef" on just any compiler you try, and then you spot THIS. FFS. Apparently this is how I was porting shaders in 2013, but not all, I was mostly sane, wrapping things in a nice ifdef EXPLICIT_UNIFORM_LOCATION, except this one case in b9a72bd3d1fd2bf763ca43a8cde7726583b671c2 where I temporarily went full retard. No idea why. --- src/Magnum/Shaders/MeshVisualizer.frag | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Magnum/Shaders/MeshVisualizer.frag b/src/Magnum/Shaders/MeshVisualizer.frag index c88460c0f..aa4889a92 100644 --- a/src/Magnum/Shaders/MeshVisualizer.frag +++ b/src/Magnum/Shaders/MeshVisualizer.frag @@ -32,10 +32,6 @@ #define const #endif -#ifndef EXPLICIT_UNIFORM_LOCATION -#define layout(arg) -#endif - #if defined(WIREFRAME_RENDERING) && defined(GL_ES) && __VERSION__ < 300 #extension GL_OES_standard_derivatives : enable #endif From 2c578cb4dfd956b666b904b9a085e6fd019e46e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 24 May 2021 23:11:05 +0200 Subject: [PATCH 32/93] Shaders: ah so this wasn't a Mesa bug, it was my stupidity. I just put this aside when I discovered the error, thinking it was a Mesa bug. Now that ARM Mali yelled about the same, I realized it wasn't just Mesa. Note to self: Mesa has no bugs. Can you just finally accept that?! --- doc/changelog.dox | 3 +++ src/Magnum/Shaders/MeshVisualizerGL.cpp | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 549229cff..99db5d87e 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -403,6 +403,9 @@ See also: - For meshes with multiple sets of vertex attributes (such as texture coordinates), @ref MeshTools::compile() should be using only the first set but it wasn't. +- @ref Shaders::MeshVisualizerGL3D "Shaders::MeshVisualizerGL*D" shader + compilation failed with missing @glsl gl_PrimitiveID @ce due to GLSL 3.20 + not being properly used for the @glsl #version @ce directive - @ref Shaders::PhongGL was normalizing light direction in vertex shader, causing the fragment-interpolated direction being incorrect with visible artifacts on long polygons under low light angle diff --git a/src/Magnum/Shaders/MeshVisualizerGL.cpp b/src/Magnum/Shaders/MeshVisualizerGL.cpp index 6d920a597..6bd1b7200 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.cpp +++ b/src/Magnum/Shaders/MeshVisualizerGL.cpp @@ -137,7 +137,8 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra /* Extended in MeshVisualizerGL3D for TBN visualization */ CORRADE_INTERNAL_ASSERT(!(_flags & FlagBase::Wireframe) || _flags & FlagBase::NoGeometryShader || version >= GL::Version::GL320); #elif !defined(MAGNUM_TARGET_WEBGL) - const GL::Version version = context.supportedVersion({GL::Version::GLES310, GL::Version::GLES300, GL::Version::GLES200}); + /* ES 3.2 needed for gl_PrimitiveID */ + const GL::Version version = context.supportedVersion({GL::Version::GLES320, GL::Version::GLES310, GL::Version::GLES300, GL::Version::GLES200}); /* Extended in MeshVisualizerGL3D for TBN visualization */ CORRADE_INTERNAL_ASSERT(!(_flags & FlagBase::Wireframe) || _flags & FlagBase::NoGeometryShader || version >= GL::Version::GLES310); #else From a5f662072dcc9d16843e5f13d8f66b80f6835e58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 23 May 2021 16:00:09 +0200 Subject: [PATCH 33/93] Shaders: update notes about what platforms got tested. Besides expanding the tested platform set and updating thresholds where needed, it makes more sense to list what is tested than what is not, because when I forget to update the list it looks like I tested while I did not. --- .../Test/DistanceFieldVectorGLTest.cpp | 38 +++++++--- src/Magnum/Shaders/Test/FlatGLTest.cpp | 44 +++++++---- .../Shaders/Test/MeshVisualizerGLTest.cpp | 76 ++++++++++++++----- src/Magnum/Shaders/Test/PhongGLTest.cpp | 55 +++++++++----- src/Magnum/Shaders/Test/VectorGLTest.cpp | 38 +++++++--- src/Magnum/Shaders/Test/VertexColorGLTest.cpp | 37 ++++++--- 6 files changed, 204 insertions(+), 84 deletions(-) diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp index 6319f8d4a..055cde641 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp @@ -126,15 +126,27 @@ struct DistanceFieldVectorGLTest: GL::OpenGLTester { }; /* - Rendering tests done on: - - - Mesa Intel - - Mesa AMD - - Mesa llvmpipe - - SwiftShader ES2/ES3 - - ARM Mali (Huawei P10) ES2/ES3 - - WebGL 1 / 2 (on Mesa Intel) - - iPhone 6 w/ iOS 12.4 + Rendering tests done: + + [B] base + [O] draw offset + + Mesa Intel BO + ES2 x + ES3 BO + Mesa AMD B + Mesa llvmpipe B + SwiftShader ES2 Bx + ES3 B + ARM Mali (Huawei P10) ES2 Bx + ES3 BO + WebGL (on Mesa Intel) 1.0 Bx + 2.0 BO + NVidia + Intel Windows + AMD macOS + Intel macOS BO + iPhone 6 w/ iOS 12.4 ES3 B */ using namespace Math::Literals; @@ -206,9 +218,13 @@ constexpr struct { Float maxThreshold, meanThreshold; } RenderMultiData[] { {"bind with offset", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", - 1, 1, 16, 0.0f, 0.0f}, + 1, 1, 16, + /* Minor differences on ARM Mali */ + 1.67f, 0.012f}, {"draw offset", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", - 2, 3, 1, 0.0f, 0.0f}, + 2, 3, 1, + /* Minor differences on ARM Mali */ + 1.67f, 0.012f}, }; #endif diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index 39f2eeaa2..6d5d4f093 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -155,18 +155,30 @@ struct FlatGLTest: GL::OpenGLTester { }; /* - Rendering tests done on: - - - Mesa Intel - - Mesa AMD - - Mesa llvmpipe - - SwiftShader ES2/ES3 - - 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) + Rendering tests done: + + [B] base + [A] alpha mask + [D] object ID + [I] instancing + [O] draw offset + + Mesa Intel BADIO + ES2 x + ES3 BADIO + Mesa AMD BADI + Mesa llvmpipe BADI + SwiftShader ES2 BADIx + ES3 BADI + ARM Mali (Huawei P10) ES2 BAD x + ES3 BADIO + WebGL (on Mesa Intel) 1.0 BAD x + 2.0 BADIO + NVidia BAD + Intel Windows BAD + AMD macOS BAD + Intel macOS BADIO + iPhone 6 w/ iOS 12.4 ES3 BAD */ using namespace Math::Literals; @@ -274,12 +286,16 @@ constexpr struct { {}, 1, 16, 0.0f, 0.0f}, {"bind with offset, textured", "multidraw-textured2D.tga", "multidraw-textured3D.tga", FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured, - 1, 16, 0.0f, 0.0f}, + 1, 16, + /* Minor differences on ARM Mali */ + 2.34f, 0.01f}, {"draw offset, colored", "multidraw2D.tga", "multidraw3D.tga", {}, 3, 1, 0.0f, 0.0f}, {"draw offset, textured", "multidraw-textured2D.tga", "multidraw-textured3D.tga", FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured, - 3, 1, 0.0f, 0.0f} + 3, 1, + /* Minor differences on ARM Mali */ + 2.34f, 0.01f} }; #endif diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index 3aec29ec1..8e7d462c1 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -174,15 +174,29 @@ struct MeshVisualizerGLTest: GL::OpenGLTester { }; /* - Rendering tests done on: - - - Mesa Intel - - Mesa AMD - . Mesa llvmpipe - - SwiftShader ES2/ES3 - - ARM Mali (Huawei P10) ES2/ES3 (except TBN visualization) - - WebGL 1 / 2 (on Mesa Intel) (except primitive/vertex/object ID) - - iPhone 6 w/ iOS 12.4 (except primitive/vertex/object ID) + Rendering tests done: + + [W] wireframe + [D] primitive/vertex/object ID + [T] TBN visualization + [O] draw offset + + Mesa Intel WDTO + ES2 x + ES3 + Mesa AMD WDT + Mesa llvmpipe WDT + SwiftShader ES2 WDxx + ES3 WDx + ARM Mali (Huawei P10) ES2 W xx + ES3 W O (WDT big diffs, needs investigation) + WebGL (on Mesa Intel) 1.0 W xx + 2.0 W x + NVidia + Intel Windows + AMD macOS + Intel macOS WDTO + iPhone 6 w/ iOS 12.4 ES3 W x */ using namespace Math::Literals; @@ -554,25 +568,37 @@ constexpr struct { #ifndef MAGNUM_TARGET_WEBGL {"bind with offset, wireframe", "multidraw-wireframe2D.tga", MeshVisualizerGL2D::Flag::Wireframe, - 1, 1, 16, 0.0f, 0.0f}, + 1, 1, 16, + /* Minor differences on ARM Mali */ + 0.67f, 0.01f}, #endif {"bind with offset, w/o GS", "multidraw-wireframe-nogeo2D.tga", MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, - 1, 1, 16, 0.0f, 0.0f}, + 1, 1, 16, + /* Minor differences on ARM Mali */ + 0.67f, 0.02f}, {"bind with offset, vertex ID", "multidraw-vertexid2D.tga", MeshVisualizerGL2D::Flag::VertexId, - 1, 1, 16, 0.0f, 0.0f}, + 1, 1, 16, + /* Minor differences on ARM Mali */ + 0.67f, 0.01f}, #ifndef MAGNUM_TARGET_WEBGL {"draw offset, wireframe", "multidraw-wireframe2D.tga", MeshVisualizerGL2D::Flag::Wireframe, - 2, 3, 1, 0.0f, 0.0f}, + 2, 3, 1, + /* Minor differences on ARM Mali */ + 0.67f, 0.01f}, #endif {"draw offset, wireframe w/o GS", "multidraw-wireframe-nogeo2D.tga", MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, - 2, 3, 1, 0.0f, 0.0f}, + 2, 3, 1, + /* Minor differences on ARM Mali */ + 0.67f, 0.02f}, {"draw offset, vertex ID", "multidraw-vertexid2D.tga", MeshVisualizerGL2D::Flag::VertexId, - 2, 3, 1, 0.0f, 0.0f} + 2, 3, 1, + /* Minor differences on ARM Mali */ + 0.67f, 0.01f}, }; constexpr struct { @@ -593,10 +619,14 @@ constexpr struct { #endif {"bind with offset, wireframe w/o GS", "multidraw-wireframe-nogeo3D.tga", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, - 1, 1, 16, 0.0f, 0.0f}, + 1, 1, 16, + /* Minor differences on ARM Mali */ + 6.0f, 0.04f}, {"bind with offset, vertex ID", "multidraw-vertexid3D.tga", MeshVisualizerGL3D::Flag::VertexId, - 1, 1, 16, 0.0f, 0.0f}, + 1, 1, 16, + /* Minor differences on ARM Mali */ + 0.67f, 0.01f}, #ifndef MAGNUM_TARGET_WEBGL {"draw offset, wireframe", "multidraw-wireframe3D.tga", MeshVisualizerGL3D::Flag::Wireframe, @@ -607,10 +637,14 @@ constexpr struct { #endif {"draw offset, wireframe w/o GS", "multidraw-wireframe-nogeo3D.tga", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, - 2, 3, 1, 0.0f, 0.0f}, + 2, 3, 1, + /* Minor differences on ARM Mali */ + 6.0f, 0.04f}, {"draw offset, vertex ID", "multidraw-vertexid3D.tga", MeshVisualizerGL3D::Flag::VertexId, - 2, 3, 1, 0.0f, 0.0f} + 2, 3, 1, + /* Minor differences on ARM Mali */ + 0.67f, 0.01f}, }; #endif @@ -3084,8 +3118,8 @@ template void MeshVisualizerGLTest::renderTangent Float maxThreshold = 1.334f, meanThreshold = 0.018f; #ifdef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) { - maxThreshold = 39.0f; - meanThreshold = 1.207f; + maxThreshold = 58.0f; + meanThreshold = 1.547f; } #endif CORRADE_COMPARE_WITH( diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 6c141d5fd..c82f7944f 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -162,18 +162,31 @@ struct PhongGLTest: GL::OpenGLTester { }; /* - Rendering tests done on: - - - Mesa Intel - - Mesa AMD - . Mesa llvmpipe - - SwiftShader ES2/ES3 - - 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) + Rendering tests done: + + [B] base + [A] alpha mask + [D] object ID + [L] point lights + [I] instancing + [O] draw offset + + Mesa Intel BADLIO + ES2 x + ES3 BADLIO + Mesa AMD BAD I + Mesa llvmpipe BAD I + SwiftShader ES2 BADLIx + ES3 BADLI + ARM Mali (Huawei P10) ES2 BAD x + ES3 BADLIO + WebGL (on Mesa Intel) 1.0 BAD x + 2.0 BADLIO + NVidia BAD + Intel Windows BAD + AMD macOS BAD + Intel macOS BADLIO + iPhone 6 w/ iOS 12.4 ES3 BAD */ constexpr struct { @@ -565,16 +578,24 @@ constexpr struct { } RenderMultiData[] { {"bind with offset, colored", "multidraw.tga", {}, - 2, 1, 1, 16, 0.0f, 0.0f}, + 2, 1, 1, 16, + /* Minor differences on ARM Mali */ + 3.34f, 0.01f}, {"bind with offset, textured", "multidraw-textured.tga", PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture, - 2, 1, 1, 16, 0.0f, 0.0f}, + 2, 1, 1, 16, + /* Minor differences on ARM Mali */ + 4.67f, 0.02f}, {"draw offset, colored", "multidraw.tga", {}, - 4, 2, 3, 1, 0.0f, 0.0f}, + 4, 2, 3, 1, + /* Minor differences on ARM Mali */ + 3.34f, 0.01f}, {"draw offset, textured", "multidraw-textured.tga", PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture, - 4, 2, 3, 1, 0.0f, 0.0f} + 4, 2, 3, 1, + /* Minor differences on ARM Mali */ + 4.67f, 0.02f}, }; #endif @@ -2022,7 +2043,7 @@ template void PhongGLTest::renderShininess() { #elif !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) /* SwiftShader has some minor rounding differences (max = 1.67). ARM Mali G71 has bigger rounding differences. */ - const Float maxThreshold = 12.0f, meanThreshold = 0.043f; + const Float maxThreshold = 221.0f, meanThreshold = 0.106f; #else /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ const Float maxThreshold = 16.667f, meanThreshold = 2.583f; diff --git a/src/Magnum/Shaders/Test/VectorGLTest.cpp b/src/Magnum/Shaders/Test/VectorGLTest.cpp index 0d492b8e1..04ba121a9 100644 --- a/src/Magnum/Shaders/Test/VectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VectorGLTest.cpp @@ -125,15 +125,27 @@ struct VectorGLTest: GL::OpenGLTester { }; /* - Rendering tests done on: - - - Mesa Intel - - Mesa AMD - - Mesa llvmpipe - - SwiftShader ES2/ES3 - - ARM Mali (Huawei P10) ES2/ES3 - - WebGL 1 / 2 (on Mesa Intel) - - iPhone 6 w/ iOS 12.4 + Rendering tests done: + + [B] base + [O] draw offset + + Mesa Intel BO + ES2 x + ES3 BO + Mesa AMD B + Mesa llvmpipe B + SwiftShader ES2 Bx + ES3 B + ARM Mali (Huawei P10) ES2 Bx + ES3 BO + WebGL (on Mesa Intel) 1.0 Bx + 2.0 BO + NVidia + Intel Windows + AMD macOS + Intel macOS BO + iPhone 6 w/ iOS 12.4 ES3 B */ using namespace Math::Literals; @@ -188,9 +200,13 @@ constexpr struct { Float maxThreshold, meanThreshold; } RenderMultiData[] { {"bind with offset", "multidraw2D.tga", "multidraw3D.tga", - 1, 16, 0.0f, 0.0f}, + 1, 16, + /* Minor differences on ARM Mali */ + 1.34f, 0.02f}, {"draw offset", "multidraw2D.tga", "multidraw3D.tga", - 3, 1, 0.0f, 0.0f}, + 3, 1, + /* Minor differences on ARM Mali */ + 1.34f, 0.02f}, }; #endif diff --git a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp index e9011fe62..3179ed70f 100644 --- a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp @@ -112,14 +112,27 @@ struct VertexColorGLTest: GL::OpenGLTester { }; /* - Rendering tests done on: - - - Mesa Intel - - Mesa AMD - - SwiftShader ES2/ES3 - - ARM Mali (Huawei P10) ES2/ES3 - - WebGL 1 / 2 (on Mesa Intel) - - iPhone 6 w/ iOS 12.4 + Rendering tests done: + + [B] base + [O] draw offset + + Mesa Intel BO + ES2 x + ES3 BO + Mesa AMD B + Mesa llvmpipe B + SwiftShader ES2 Bx + ES3 B + ARM Mali (Huawei P10) ES2 Bx + ES3 BO + WebGL (on Mesa Intel) 1.0 Bx + 2.0 BO + NVidia + Intel Windows + AMD macOS + Intel macOS BO + iPhone 6 w/ iOS 12.4 ES3 B */ using namespace Math::Literals; @@ -148,9 +161,13 @@ constexpr struct { Float maxThreshold, meanThreshold; } RenderMultiData[] { {"bind with offset", "multidraw2D.tga", "multidraw3D.tga", - 1, 16, 0.0f, 0.0f}, + 1, 16, + /* Minor differences on ARM Mali */ + 0.34f, 0.01f}, {"draw offset", "multidraw2D.tga", "multidraw3D.tga", - 3, 1, 0.0f, 0.0f} + 3, 1, + /* Minor differences on ARM Mali */ + 0.34f, 0.01f}, }; #endif From 63a8df10759f42d186746f10a8be9aff8ee63831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 25 May 2021 18:50:23 +0200 Subject: [PATCH 34/93] CMake: make dynamic plugins dependencies of tests that use CompareImage. So when building just a particular test, the relevant plugins get implicitly built as well without having to build everything. --- src/Magnum/DebugTools/Test/CMakeLists.txt | 38 ++++++++++++++++ src/Magnum/GL/Test/CMakeLists.txt | 8 ++++ src/Magnum/MeshTools/Test/CMakeLists.txt | 8 ++++ src/Magnum/Shaders/Test/CMakeLists.txt | 48 +++++++++++++++++++++ src/Magnum/TextureTools/Test/CMakeLists.txt | 8 ++++ src/Magnum/Vk/Test/CMakeLists.txt | 8 ++++ 6 files changed, 118 insertions(+) diff --git a/src/Magnum/DebugTools/Test/CMakeLists.txt b/src/Magnum/DebugTools/Test/CMakeLists.txt index b42d79ded..764c463eb 100644 --- a/src/Magnum/DebugTools/Test/CMakeLists.txt +++ b/src/Magnum/DebugTools/Test/CMakeLists.txt @@ -88,6 +88,20 @@ if(WITH_TRADE) if(WITH_TGAIMPORTER) target_link_libraries(DebugToolsCompareImageTest PRIVATE TgaImporter) endif() + else() + # So the plugins get properly built when building the test + if(WITH_ANYIMAGECONVERTER) + add_dependencies(DebugToolsCompareImageTest AnyImageConverter) + endif() + if(WITH_ANYIMAGEIMPORTER) + add_dependencies(DebugToolsCompareImageTest AnyImageImporter) + endif() + if(WITH_TGAIMAGECONVERTER) + add_dependencies(DebugToolsCompareImageTest TgaImageConverter) + endif() + if(WITH_TGAIMPORTER) + add_dependencies(DebugToolsCompareImageTest TgaImporter) + endif() endif() endif() @@ -130,6 +144,20 @@ if(TARGET_GL) if(WITH_TGAIMPORTER) target_link_libraries(DebugToolsScreenshotGLTest PRIVATE TgaImporter) endif() + else() + # So the plugins get properly built when building the test + if(WITH_ANYIMAGECONVERTER) + add_dependencies(DebugToolsScreenshotGLTest AnyImageConverter) + endif() + if(WITH_ANYIMAGEIMPORTER) + add_dependencies(DebugToolsScreenshotGLTest AnyImageImporter) + endif() + if(WITH_TGAIMAGECONVERTER) + add_dependencies(DebugToolsScreenshotGLTest TgaImageConverter) + endif() + if(WITH_TGAIMPORTER) + add_dependencies(DebugToolsScreenshotGLTest TgaImporter) + endif() endif() if(CORRADE_BUILD_STATIC AND NOT BUILD_PLUGINS_STATIC) @@ -164,6 +192,16 @@ if(TARGET_GL) target_link_libraries(DebugToolsForceRendererGLTest PRIVATE TgaImporter) target_link_libraries(DebugToolsObjectRendererGLTest PRIVATE TgaImporter) endif() + else() + # So the plugins get properly built when building the test + if(WITH_ANYIMAGEIMPORTER) + add_dependencies(DebugToolsForceRendererGLTest AnyImageImporter) + add_dependencies(DebugToolsObjectRendererGLTest AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + add_dependencies(DebugToolsForceRendererGLTest TgaImporter) + add_dependencies(DebugToolsObjectRendererGLTest TgaImporter) + endif() endif() endif() endif() diff --git a/src/Magnum/GL/Test/CMakeLists.txt b/src/Magnum/GL/Test/CMakeLists.txt index 37aac840a..60e0de0b2 100644 --- a/src/Magnum/GL/Test/CMakeLists.txt +++ b/src/Magnum/GL/Test/CMakeLists.txt @@ -168,6 +168,14 @@ if(BUILD_GL_TESTS) if(WITH_TGAIMPORTER) target_link_libraries(GLRendererGLTest PRIVATE TgaImporter) endif() + else() + # So the plugins get properly built when building the test + if(WITH_ANYIMAGEIMPORTER) + add_dependencies(GLRendererGLTest AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + add_dependencies(GLRendererGLTest TgaImporter) + endif() endif() corrade_add_test(GLShaderGLTest ShaderGLTest.cpp diff --git a/src/Magnum/MeshTools/Test/CMakeLists.txt b/src/Magnum/MeshTools/Test/CMakeLists.txt index 97cb61b0f..6d7f421ef 100644 --- a/src/Magnum/MeshTools/Test/CMakeLists.txt +++ b/src/Magnum/MeshTools/Test/CMakeLists.txt @@ -133,5 +133,13 @@ if(BUILD_GL_TESTS) if(WITH_TGAIMPORTER) target_link_libraries(MeshToolsCompileGLTest PRIVATE TgaImporter) endif() + else() + # So the plugins get properly built when building the test + if(WITH_ANYIMAGEIMPORTER) + add_dependencies(MeshToolsCompileGLTest AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + add_dependencies(MeshToolsCompileGLTest TgaImporter) + endif() endif() endif() diff --git a/src/Magnum/Shaders/Test/CMakeLists.txt b/src/Magnum/Shaders/Test/CMakeLists.txt index c99df3824..da3ceb234 100644 --- a/src/Magnum/Shaders/Test/CMakeLists.txt +++ b/src/Magnum/Shaders/Test/CMakeLists.txt @@ -121,6 +121,14 @@ if(BUILD_GL_TESTS) if(WITH_TGAIMPORTER) target_link_libraries(ShadersDistanceFieldVectorGLTest PRIVATE TgaImporter) endif() + else() + # So the plugins get properly built when building the test + if(WITH_ANYIMAGEIMPORTER) + add_dependencies(ShadersDistanceFieldVectorGLTest AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + add_dependencies(ShadersDistanceFieldVectorGLTest TgaImporter) + endif() endif() set(ShadersFlatGLTest_SRCS FlatGLTest.cpp) @@ -164,6 +172,14 @@ if(BUILD_GL_TESTS) if(WITH_TGAIMPORTER) target_link_libraries(ShadersFlatGLTest PRIVATE TgaImporter) endif() + else() + # So the plugins get properly built when building the test + if(WITH_ANYIMAGEIMPORTER) + add_dependencies(ShadersFlatGLTest AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + add_dependencies(ShadersFlatGLTest TgaImporter) + endif() endif() set(ShadersMeshVisualizerGLTest_SRCS MeshVisualizerGLTest.cpp) @@ -227,6 +243,14 @@ if(BUILD_GL_TESTS) if(WITH_TGAIMPORTER) target_link_libraries(ShadersMeshVisualizerGLTest PRIVATE TgaImporter) endif() + else() + # So the plugins get properly built when building the test + if(WITH_ANYIMAGEIMPORTER) + add_dependencies(ShadersMeshVisualizerGLTest AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + add_dependencies(ShadersMeshVisualizerGLTest TgaImporter) + endif() endif() set(ShadersPhongGLTest_SRCS PhongGLTest.cpp) @@ -293,6 +317,14 @@ if(BUILD_GL_TESTS) if(WITH_TGAIMPORTER) target_link_libraries(ShadersPhongGLTest PRIVATE TgaImporter) endif() + else() + # So the plugins get properly built when building the test + if(WITH_ANYIMAGEIMPORTER) + add_dependencies(ShadersPhongGLTest AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + add_dependencies(ShadersPhongGLTest TgaImporter) + endif() endif() set(ShadersVectorGLTest_SRCS VectorGLTest.cpp) @@ -322,6 +354,14 @@ if(BUILD_GL_TESTS) if(WITH_TGAIMPORTER) target_link_libraries(ShadersVectorGLTest PRIVATE TgaImporter) endif() + else() + # So the plugins get properly built when building the test + if(WITH_ANYIMAGEIMPORTER) + add_dependencies(ShadersVectorGLTest AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + add_dependencies(ShadersVectorGLTest TgaImporter) + endif() endif() set(ShadersVertexColorGLTest_SRCS VertexColorGLTest.cpp) @@ -350,6 +390,14 @@ if(BUILD_GL_TESTS) if(WITH_TGAIMPORTER) target_link_libraries(ShadersVertexColorGLTest PRIVATE TgaImporter) endif() + else() + # So the plugins get properly built when building the test + if(WITH_ANYIMAGEIMPORTER) + add_dependencies(ShadersVertexColorGLTest AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + add_dependencies(ShadersVertexColorGLTest TgaImporter) + endif() endif() corrade_add_test(ShadersGLBenchmark ShadersGLBenchmark.cpp diff --git a/src/Magnum/TextureTools/Test/CMakeLists.txt b/src/Magnum/TextureTools/Test/CMakeLists.txt index a3e67a301..cbabb8193 100644 --- a/src/Magnum/TextureTools/Test/CMakeLists.txt +++ b/src/Magnum/TextureTools/Test/CMakeLists.txt @@ -77,5 +77,13 @@ if(BUILD_GL_TESTS) if(WITH_TGAIMPORTER) target_link_libraries(TextureToolsDistanceFieldGLTest PRIVATE TgaImporter) endif() + else() + # So the plugins get properly built when building the test + if(WITH_ANYIMAGEIMPORTER) + add_dependencies(TextureToolsDistanceFieldGLTest AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + add_dependencies(TextureToolsDistanceFieldGLTest TgaImporter) + endif() endif() endif() diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 2c21967f2..4ea7d965f 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -236,6 +236,14 @@ if(BUILD_VK_TESTS) if(WITH_TGAIMPORTER) target_link_libraries(VkMeshVkTest PRIVATE TgaImporter) endif() + else() + # So the plugins get properly built when building the test + if(WITH_ANYIMAGEIMPORTER) + add_dependencies(VkMeshVkTest AnyImageImporter) + endif() + if(WITH_TGAIMPORTER) + add_dependencies(VkMeshVkTest TgaImporter) + endif() endif() corrade_add_test(VkPipelineVkTest PipelineVkTest.cpp From 5d7a7d4d929914730a99abcff27558201bbb3f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 26 May 2021 22:04:57 +0200 Subject: [PATCH 35/93] Shaders: ensure the ObjectId and Bitangent attribs are not used together. These deliberately share the same binding (because there's very little space), but the shader wasn't guarding that. Discovered completely by accident when adding tests for "multidraw with all the things" -- Mesa gives just a warning, but ANGLE straight out fails the shader compilation, so better have an assert there. --- src/Magnum/Shaders/MeshVisualizerGL.cpp | 5 +++ src/Magnum/Shaders/PhongGL.cpp | 5 +++ .../Shaders/Test/MeshVisualizerGLTest.cpp | 10 ++++- src/Magnum/Shaders/Test/PhongGLTest.cpp | 44 +++++++++++++++---- 4 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/Magnum/Shaders/MeshVisualizerGL.cpp b/src/Magnum/Shaders/MeshVisualizerGL.cpp index 6bd1b7200..5c9488408 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.cpp +++ b/src/Magnum/Shaders/MeshVisualizerGL.cpp @@ -572,6 +572,11 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags "Shaders::MeshVisualizerGL3D: at least Flag::Wireframe has to be enabled", ); #endif + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + CORRADE_ASSERT(!(flags & Flag::InstancedObjectId) || !(flags & Flag::BitangentDirection), + "Shaders::MeshVisualizerGL3D: Bitangent attribute binding conflicts with the ObjectId attribute, use a Tangent4 attribute with instanced object ID rendering instead", ); + #endif + /* Has to be here and not in the base class in order to have it exit the constructor when testing for asserts -- GLSL compilation would fail otherwise */ diff --git a/src/Magnum/Shaders/PhongGL.cpp b/src/Magnum/Shaders/PhongGL.cpp index 87d62e4bd..00e1083da 100644 --- a/src/Magnum/Shaders/PhongGL.cpp +++ b/src/Magnum/Shaders/PhongGL.cpp @@ -89,6 +89,11 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount CORRADE_ASSERT(!(flags & Flag::TextureTransformation) || (flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture)), "Shaders::PhongGL: texture transformation enabled but the shader is not textured", ); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags & Flag::InstancedObjectId) || !(flags & Flag::Bitangent), + "Shaders::PhongGL: Bitangent attribute binding conflicts with the ObjectId attribute, use a Tangent4 attribute with instanced object ID rendering instead", ); + #endif + #ifndef MAGNUM_TARGET_GLES2 CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || materialCount, "Shaders::PhongGL: material count can't be zero", ); diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index 8e7d462c1..0b6bdbd92 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -275,7 +275,10 @@ constexpr struct { {"wireframe + vertex id", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::VertexId}, {"wireframe + t/n direction", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::NormalDirection}, {"wireframe + object id + t/n direction", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::NormalDirection}, - {"wireframe + vertex id + t/b direction", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::VertexId|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentDirection} + {"wireframe + vertex id + t/b direction", MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::VertexId|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentDirection}, + /* InstancedObjectId|BitangentDirection is disallowed (checked in + ConstructInvalidData3D), but this should work */ + {"object id + bitangent from tangent direction", MeshVisualizerGL3D::Flag::InstancedObjectId|MeshVisualizerGL3D::Flag::BitangentFromTangentDirection}, #endif }; @@ -381,7 +384,10 @@ constexpr struct { "3D: geometry shader has to be enabled when rendering TBN direction"}, {"conflicting bitangent input", MeshVisualizerGL3D::Flag::BitangentFromTangentDirection|MeshVisualizerGL3D::Flag::BitangentDirection, - "3D: Flag::BitangentDirection and Flag::BitangentFromTangentDirection are mutually exclusive"} + "3D: Flag::BitangentDirection and Flag::BitangentFromTangentDirection are mutually exclusive"}, + {"conflicting bitangent and instanced object id attribute", + MeshVisualizerGL3D::Flag::BitangentDirection|MeshVisualizerGL3D::Flag::InstancedObjectId, + "3D: Bitangent attribute binding conflicts with the ObjectId attribute, use a Tangent4 attribute with instanced object ID rendering instead"}, #endif }; diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index c82f7944f..4a952bce2 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "Magnum/Image.h" #include "Magnum/ImageView.h" @@ -58,8 +59,6 @@ #include "Magnum/Trade/MeshData.h" #ifndef MAGNUM_TARGET_GLES2 -#include - #include "Magnum/GL/MeshView.h" #include "Magnum/MeshTools/Concatenate.h" #include "Magnum/MeshTools/GenerateIndices.h" @@ -85,7 +84,7 @@ struct PhongGLTest: GL::OpenGLTester { void constructMoveUniformBuffers(); #endif - void constructTextureTransformationNotTextured(); + void constructInvalid(); #ifndef MAGNUM_TARGET_GLES2 void constructUniformBuffersInvalid(); #endif @@ -220,7 +219,12 @@ constexpr struct { {"zero lights", {}, 0}, {"instanced transformation", PhongGL::Flag::InstancedTransformation, 3}, {"instanced specular texture offset", PhongGL::Flag::SpecularTexture|PhongGL::Flag::InstancedTextureOffset, 3}, - {"instanced normal texture offset", PhongGL::Flag::NormalTexture|PhongGL::Flag::InstancedTextureOffset, 3} + {"instanced normal texture offset", PhongGL::Flag::NormalTexture|PhongGL::Flag::InstancedTextureOffset, 3}, + #ifndef MAGNUM_TARGET_GLES2 + /* InstancedObjectId|Bitangent is disallowed (checked in + ConstructInvalidData), but this should work */ + {"object ID + normal texture with bitangent from tangent", PhongGL::Flag::InstancedObjectId|PhongGL::Flag::NormalTexture, 1} + #endif }; #ifndef MAGNUM_TARGET_GLES2 @@ -242,7 +246,24 @@ constexpr struct { {"alpha mask", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AlphaMask, 1, 1, 1}, {"object ID", PhongGL::Flag::UniformBuffers|PhongGL::Flag::ObjectId, 1, 1, 1} }; +#endif +constexpr struct { + const char* name; + PhongGL::Flags flags; + const char* message; +} ConstructInvalidData[] { + {"texture transformation but not textured", + PhongGL::Flag::TextureTransformation, + "texture transformation enabled but the shader is not textured"}, + #ifndef MAGNUM_TARGET_GLES2 + {"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"}, + #endif +}; + +#ifndef MAGNUM_TARGET_GLES2 constexpr struct { const char* name; PhongGL::Flags flags; @@ -613,8 +634,10 @@ PhongGLTest::PhongGLTest() { #ifndef MAGNUM_TARGET_GLES2 &PhongGLTest::constructMoveUniformBuffers, #endif + }); - &PhongGLTest::constructTextureTransformationNotTextured}); + addInstancedTests({&PhongGLTest::constructInvalid}, + Containers::arraySize(ConstructInvalidData)); #ifndef MAGNUM_TARGET_GLES2 addInstancedTests({ @@ -922,16 +945,19 @@ void PhongGLTest::constructMoveUniformBuffers() { } #endif -void PhongGLTest::constructTextureTransformationNotTextured() { +void PhongGLTest::constructInvalid() { + auto&& data = ConstructInvalidData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif std::ostringstream out; Error redirectError{&out}; - PhongGL{PhongGL::Flag::TextureTransformation}; - CORRADE_COMPARE(out.str(), - "Shaders::PhongGL: texture transformation enabled but the shader is not textured\n"); + PhongGL{data.flags}; + CORRADE_COMPARE(out.str(), Utility::formatString( + "Shaders::PhongGL: {}\n", data.message)); } #ifndef MAGNUM_TARGET_GLES2 From 2c09a2a1e6456b01b03bc6a68de1cdc26e3508ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 26 May 2021 22:55:06 +0200 Subject: [PATCH 36/93] Shaders: multidraw support in all builtin shaders. --- doc/changelog.dox | 3 +- src/Magnum/Shaders/DistanceFieldVector.frag | 9 +- src/Magnum/Shaders/DistanceFieldVectorGL.cpp | 15 ++ src/Magnum/Shaders/DistanceFieldVectorGL.h | 34 +++- src/Magnum/Shaders/Flat.frag | 13 +- src/Magnum/Shaders/Flat.vert | 28 +++- src/Magnum/Shaders/FlatGL.cpp | 15 ++ src/Magnum/Shaders/FlatGL.h | 34 +++- src/Magnum/Shaders/MeshVisualizer.frag | 9 +- src/Magnum/Shaders/MeshVisualizer.geom | 18 +- src/Magnum/Shaders/MeshVisualizer.vert | 44 ++++- src/Magnum/Shaders/MeshVisualizerGL.cpp | 19 +++ src/Magnum/Shaders/MeshVisualizerGL.h | 65 +++++++- src/Magnum/Shaders/Phong.frag | 15 +- src/Magnum/Shaders/Phong.vert | 34 +++- src/Magnum/Shaders/PhongGL.cpp | 15 ++ src/Magnum/Shaders/PhongGL.h | 31 +++- .../Test/DistanceFieldVectorGLTest.cpp | 118 +++++++++---- .../Test/DistanceFieldVectorGL_Test.cpp | 33 +++- src/Magnum/Shaders/Test/FlatGLTest.cpp | 115 ++++++++++--- src/Magnum/Shaders/Test/FlatGL_Test.cpp | 17 +- .../Shaders/Test/MeshVisualizerGLTest.cpp | 156 +++++++++++++++--- .../Shaders/Test/MeshVisualizerGL_Test.cpp | 50 ++++-- src/Magnum/Shaders/Test/PhongGLTest.cpp | 83 +++++++--- src/Magnum/Shaders/Test/PhongGL_Test.cpp | 17 +- src/Magnum/Shaders/Test/VectorGLTest.cpp | 118 +++++++++---- src/Magnum/Shaders/Test/VectorGL_Test.cpp | 33 +++- src/Magnum/Shaders/Test/VertexColorGLTest.cpp | 120 ++++++++++---- .../Shaders/Test/VertexColorGL_Test.cpp | 18 +- src/Magnum/Shaders/Vector.frag | 11 +- src/Magnum/Shaders/Vector.vert | 28 +++- src/Magnum/Shaders/VectorGL.cpp | 15 ++ src/Magnum/Shaders/VectorGL.h | 34 +++- src/Magnum/Shaders/VertexColor.vert | 22 ++- src/Magnum/Shaders/VertexColorGL.cpp | 14 ++ src/Magnum/Shaders/VertexColorGL.h | 34 +++- 36 files changed, 1202 insertions(+), 235 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 99db5d87e..e2fc0271d 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -148,7 +148,8 @@ See also: @subsubsection changelog-latest-new-shaders Shaders library - All builtin shaders now have opt-in support for uniform buffers on desktop, - OpenGL ES 3.0+ and WebGL 2.0 + OpenGL ES 3.0+ and WebGL 2.0, including multi-draw functionality for + massive driver overhead reduction - Added @ref Shaders::PhongGL::setNormalTextureScale(), consuming the recently added @ref Trade::MaterialAttribute::NormalTextureScale material attribute diff --git a/src/Magnum/Shaders/DistanceFieldVector.frag b/src/Magnum/Shaders/DistanceFieldVector.frag index 4f292e38f..ab6733f4f 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.frag +++ b/src/Magnum/Shaders/DistanceFieldVector.frag @@ -71,6 +71,7 @@ uniform lowp float smoothness /* Uniform buffers */ #else +#ifndef MULTI_DRAW #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -79,6 +80,8 @@ uniform highp uint drawOffset = 0u #endif ; +#define drawId drawOffset +#endif struct DrawUniform { highp uvec4 materialIdReservedReservedReservedReserved; @@ -122,6 +125,10 @@ uniform lowp sampler2D vectorTexture; in mediump vec2 interpolatedTextureCoordinates; +#ifdef MULTI_DRAW +flat in highp uint drawId; +#endif + /* OUtput */ #ifdef NEW_GLSL @@ -133,7 +140,7 @@ out lowp vec4 fragmentColor; void main() { #ifdef UNIFORM_BUFFERS - mediump const uint materialId = draws[drawOffset].draw_materialIdReserved & 0xffffu; + mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; lowp const float smoothness = materials[materialId].material_smoothness; lowp const vec4 color = materials[materialId].color; lowp const vec4 outlineColor = materials[materialId].outlineColor; diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp index 4ded3988b..2a9e55175 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp @@ -85,6 +85,17 @@ template DistanceFieldVectorGL::DistanceFiel if(flags >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_draw_parameters); + #elif !defined(MAGNUM_TARGET_WEBGL) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ANGLE::multi_draw); + #else + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::WEBGL::multi_draw); + #endif + } + #endif #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ @@ -112,6 +123,7 @@ template DistanceFieldVectorGL::DistanceFiel "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", drawCount)); + vert.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif vert.addSource(rs.get("generic.glsl")) @@ -124,6 +136,7 @@ template DistanceFieldVectorGL::DistanceFiel "#define DRAW_COUNT {}\n", materialCount, drawCount)); + frag.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif frag.addSource(rs.get("generic.glsl")) @@ -351,6 +364,7 @@ Debug& operator<<(Debug& debug, const DistanceFieldVectorGLFlag value) { _c(TextureTransformation) #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + _c(MultiDraw) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -363,6 +377,7 @@ Debug& operator<<(Debug& debug, const DistanceFieldVectorGLFlags value) { return Containers::enumSetDebugOutput(debug, value, "Shaders::DistanceFieldVectorGL::Flags{}", { DistanceFieldVectorGLFlag::TextureTransformation, #ifndef MAGNUM_TARGET_GLES2 + DistanceFieldVectorGLFlag::MultiDraw, /* Superset of UniformBuffers */ DistanceFieldVectorGLFlag::UniformBuffers #endif }); diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.h b/src/Magnum/Shaders/DistanceFieldVectorGL.h index 604c6106b..4284cd7c1 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.h +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.h @@ -41,7 +41,8 @@ namespace Implementation { enum class DistanceFieldVectorGLFlag: UnsignedByte { TextureTransformation = 1 << 0, #ifndef MAGNUM_TARGET_GLES2 - UniformBuffers = 1 << 1 + UniformBuffers = 1 << 1, + MultiDraw = UniformBuffers|(1 << 2) #endif }; typedef Containers::EnumSet DistanceFieldVectorGLFlags; @@ -140,7 +141,31 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * 1.0. * @m_since_latest */ - UniformBuffers = 1 << 1 + UniformBuffers = 1 << 1, + + /** + * Enable multidraw functionality. Implies @ref Flag::UniformBuffers + * and adds the value from @ref setDrawOffset() with the + * @glsl gl_DrawID @ce builtin, which makes draws submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up per-draw parameters directly, without having to rebind + * the uniform buffers or specify @ref setDrawOffset() before each + * draw. In a non-multidraw scenario, @glsl gl_DrawID @ce is + * @cpp 0 @ce, which means a shader with this flag enabled can be + * used for regular draws as well. + * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} + * and @gl_extension{ARB,shader_draw_parameters} + * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). + * While the extension alone needs only OpenGL ES 2.0, the + * shader implementation relies on uniform buffers, which + * require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. + * While the extension alone needs only WebGL 1.0, the shader + * implementation relies on uniform buffers, which require + * WebGL 2.0. + * @m_since_latest + */ + MultiDraw = UniformBuffers|(1 << 2) #endif }; @@ -387,6 +412,11 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * @ref bindTextureTransformationBuffer() should be used for current * draw. Expects that @ref Flag::UniformBuffers is set and @p offset is * less than @ref drawCount(). Initial value is @cpp 0 @ce. + * + * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this + * value, which makes each draw submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up its own per-draw parameters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. diff --git a/src/Magnum/Shaders/Flat.frag b/src/Magnum/Shaders/Flat.frag index 613f84b05..b9fa74103 100644 --- a/src/Magnum/Shaders/Flat.frag +++ b/src/Magnum/Shaders/Flat.frag @@ -75,6 +75,7 @@ uniform highp uint objectId; /* defaults to zero */ /* Uniform buffers */ #else +#ifndef MULTI_DRAW #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -83,6 +84,8 @@ uniform highp uint drawOffset = 0u #endif ; +#define drawId drawOffset +#endif struct DrawUniform { lowp vec4 color; @@ -139,14 +142,18 @@ layout(location = OBJECT_ID_OUTPUT_ATTRIBUTE_LOCATION) out highp uint fragmentObjectId; #endif +#ifdef MULTI_DRAW +flat in highp uint drawId; +#endif + void main() { #ifdef UNIFORM_BUFFERS - lowp const vec4 color = draws[drawOffset].color; + lowp const vec4 color = draws[drawId].color; #ifdef OBJECT_ID - highp const uint objectId = draws[drawOffset].draw_objectId; + highp const uint objectId = draws[drawId].draw_objectId; #endif #ifdef ALPHA_MASK - lowp const float alphaMask = uintBitsToFloat(draws[drawOffset].draw_alphaMask); + lowp const float alphaMask = uintBitsToFloat(draws[drawId].draw_alphaMask); #endif #endif diff --git a/src/Magnum/Shaders/Flat.vert b/src/Magnum/Shaders/Flat.vert index 93fa99e1b..0cf02e95d 100644 --- a/src/Magnum/Shaders/Flat.vert +++ b/src/Magnum/Shaders/Flat.vert @@ -27,6 +27,14 @@ #extension GL_EXT_gpu_shader4: require #endif +#ifdef MULTI_DRAW +#ifndef GL_ES +#extension GL_ARB_shader_draw_parameters: require +#else /* covers WebGL as well */ +#extension GL_ANGLE_multi_draw: require +#endif +#endif + #ifndef NEW_GLSL #define in attribute #define out varying @@ -182,8 +190,24 @@ out lowp vec4 interpolatedVertexColor; flat out highp uint interpolatedInstanceObjectId; #endif +#ifdef MULTI_DRAW +flat out highp uint drawId; +#endif + void main() { #ifdef UNIFORM_BUFFERS + #ifdef MULTI_DRAW + drawId = drawOffset + uint( + #ifndef GL_ES + gl_DrawIDARB /* Using GL_ARB_shader_draw_parameters, not GLSL 4.6 */ + #else + gl_DrawID + #endif + ); + #else + #define drawId drawOffset + #endif + highp const #ifdef TWO_DIMENSIONS mat3 @@ -192,9 +216,9 @@ void main() { #else #error #endif - transformationProjectionMatrix = transformationProjectionMatrices[drawOffset]; + transformationProjectionMatrix = transformationProjectionMatrices[drawId]; #ifdef TEXTURE_TRANSFORMATION - mediump const mat3 textureMatrix = mat3(textureTransformations[drawOffset].rotationScaling.xy, 0.0, textureTransformations[drawOffset].rotationScaling.zw, 0.0, textureTransformations[drawOffset].textureTransformation_offset, 1.0); + mediump const mat3 textureMatrix = mat3(textureTransformations[drawId].rotationScaling.xy, 0.0, textureTransformations[drawId].rotationScaling.zw, 0.0, textureTransformations[drawId].textureTransformation_offset, 1.0); #endif #endif diff --git a/src/Magnum/Shaders/FlatGL.cpp b/src/Magnum/Shaders/FlatGL.cpp index 5887f781c..c91221fb6 100644 --- a/src/Magnum/Shaders/FlatGL.cpp +++ b/src/Magnum/Shaders/FlatGL.cpp @@ -84,6 +84,17 @@ template FlatGL::FlatGL(const Flags flags if(flags >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_draw_parameters); + #elif !defined(MAGNUM_TARGET_WEBGL) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ANGLE::multi_draw); + #else + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::WEBGL::multi_draw); + #endif + } + #endif #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ @@ -118,6 +129,7 @@ template FlatGL::FlatGL(const Flags flags "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", drawCount)); + vert.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif vert.addSource(rs.get("generic.glsl")) @@ -136,6 +148,7 @@ template FlatGL::FlatGL(const Flags flags "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", drawCount)); + frag.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif frag.addSource(rs.get("generic.glsl")) @@ -370,6 +383,7 @@ Debug& operator<<(Debug& debug, const FlatGLFlag value) { _c(InstancedTextureOffset) #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + _c(MultiDraw) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -391,6 +405,7 @@ Debug& operator<<(Debug& debug, const FlatGLFlags value) { #endif FlatGLFlag::InstancedTransformation, #ifndef MAGNUM_TARGET_GLES2 + FlatGLFlag::MultiDraw, /* Superset of UniformBuffers */ FlatGLFlag::UniformBuffers #endif }); diff --git a/src/Magnum/Shaders/FlatGL.h b/src/Magnum/Shaders/FlatGL.h index d052ca5e7..1daa1c6c4 100644 --- a/src/Magnum/Shaders/FlatGL.h +++ b/src/Magnum/Shaders/FlatGL.h @@ -50,7 +50,8 @@ namespace Implementation { InstancedTransformation = 1 << 6, InstancedTextureOffset = (1 << 7)|TextureTransformation, #ifndef MAGNUM_TARGET_GLES2 - UniformBuffers = 1 << 8 + UniformBuffers = 1 << 8, + MultiDraw = UniformBuffers|(1 << 9) #endif }; typedef Containers::EnumSet FlatGLFlags; @@ -388,7 +389,31 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * 1.0. * @m_since_latest */ - UniformBuffers = 1 << 8 + UniformBuffers = 1 << 8, + + /** + * Enable multidraw functionality. Implies @ref Flag::UniformBuffers + * and adds the value from @ref setDrawOffset() with the + * @glsl gl_DrawID @ce builtin, which makes draws submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up per-draw parameters directly, without having to rebind + * the uniform buffers or specify @ref setDrawOffset() before each + * draw. In a non-multidraw scenario, @glsl gl_DrawID @ce is + * @cpp 0 @ce, which means a shader with this flag enabled can be + * used for regular draws as well. + * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} + * and @gl_extension{ARB,shader_draw_parameters} + * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). + * While the extension alone needs only OpenGL ES 2.0, the + * shader implementation relies on uniform buffers, which + * require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. + * While the extension alone needs only WebGL 1.0, the shader + * implementation relies on uniform buffers, which require + * WebGL 2.0. + * @m_since_latest + */ + MultiDraw = UniformBuffers|(1 << 9) #endif }; @@ -602,6 +627,11 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * @ref bindTextureTransformationBuffer() should be used for current * draw. Expects that @ref Flag::UniformBuffers is set and @p offset is * less than @ref drawCount(). Initial value is @cpp 0 @ce. + * + * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this + * value, which makes each draw submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up its own per-draw parameters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. diff --git a/src/Magnum/Shaders/MeshVisualizer.frag b/src/Magnum/Shaders/MeshVisualizer.frag index aa4889a92..6987931ba 100644 --- a/src/Magnum/Shaders/MeshVisualizer.frag +++ b/src/Magnum/Shaders/MeshVisualizer.frag @@ -110,6 +110,7 @@ uniform lowp vec2 colorMapOffsetScale /* Uniform buffers */ #else +#ifndef MULTI_DRAW #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 1) #endif @@ -118,6 +119,8 @@ uniform highp uint drawOffset = 0u #endif ; +#define drawId drawOffset +#endif /* Keep in sync with MeshVisualizer.vert and MeshVisualizer.geom. Can't "outsource" to a common file because the extension directives need to be @@ -202,6 +205,10 @@ in lowp vec4 backgroundColor; in lowp vec4 lineColor; #endif +#ifdef MULTI_DRAW +flat in highp uint drawId; +#endif + /* Outputs */ #ifdef NEW_GLSL @@ -213,7 +220,7 @@ out lowp vec4 fragmentColor; void main() { #ifdef UNIFORM_BUFFERS - mediump const uint materialId = draws[drawOffset].draw_materialIdReserved & 0xffffu; + mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; #if (defined(WIREFRAME_RENDERING) || defined(INSTANCED_OBJECT_ID) || defined(VERTEX_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID)) && !defined(TBN_DIRECTION) lowp const vec4 color = materials[materialId].color; lowp const vec4 wireframeColor = materials[materialId].wireframeColor; diff --git a/src/Magnum/Shaders/MeshVisualizer.geom b/src/Magnum/Shaders/MeshVisualizer.geom index 89709595f..3cfe14402 100644 --- a/src/Magnum/Shaders/MeshVisualizer.geom +++ b/src/Magnum/Shaders/MeshVisualizer.geom @@ -87,6 +87,7 @@ uniform lowp float smoothness /* Uniform buffers */ #else +#ifndef MULTI_DRAW #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 1) #endif @@ -95,6 +96,7 @@ uniform highp uint drawOffset = 0u #endif ; +#endif /* Keep in sync with MeshVisualizer.vert and MeshVisualizer.frag. Can't "outsource" to a common file because the #extension directives need to be @@ -166,6 +168,10 @@ in highp float interpolatedVsMappedVertexId[]; flat in highp uint interpolatedVsPrimitiveId[]; #endif +#ifdef MULTI_DRAW +flat in highp uint vsDrawId[]; +#endif + /* Outputs */ layout(triangle_strip, max_vertices = MAX_VERTICES) out; @@ -186,6 +192,10 @@ out highp float interpolatedMappedVertexId; flat out highp uint interpolatedPrimitiveId; #endif +#ifdef MULTI_DRAW +flat out highp uint drawId; +#endif + #if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION) out lowp vec4 backgroundColor; out lowp vec4 lineColor; @@ -255,7 +265,13 @@ void emitQuad( void main() { #ifdef UNIFORM_BUFFERS - mediump const uint materialId = draws[drawOffset].draw_materialIdReserved & 0xffffu; + #ifdef MULTI_DRAW + drawId = vsDrawId[0]; + #else + #define drawId drawOffset + #endif + + mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; #if (defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION)) && (defined(WIREFRAME_RENDERING) || defined(INSTANCED_OBJECT_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID)) lowp const vec4 color = materials[materialId].color; lowp const vec4 wireframeColor = materials[materialId].wireframeColor; diff --git a/src/Magnum/Shaders/MeshVisualizer.vert b/src/Magnum/Shaders/MeshVisualizer.vert index 38ce85ffb..6aa9ebe7b 100644 --- a/src/Magnum/Shaders/MeshVisualizer.vert +++ b/src/Magnum/Shaders/MeshVisualizer.vert @@ -27,6 +27,14 @@ #extension GL_EXT_gpu_shader4: require #endif +#ifdef MULTI_DRAW +#ifndef GL_ES +#extension GL_ARB_shader_draw_parameters: require +#else /* covers WebGL as well */ +#extension GL_ANGLE_multi_draw: require +#endif +#endif + #ifndef NEW_GLSL #define in attribute #define out varying @@ -279,19 +287,47 @@ out highp vec4 bitangentEndpoint; out highp vec4 normalEndpoint; #endif +#ifdef MULTI_DRAW +flat out highp uint + #ifdef NO_GEOMETRY_SHADER + drawId + #else + vsDrawId + #endif + ; +#endif + void main() { #ifdef UNIFORM_BUFFERS + #ifdef MULTI_DRAW + #ifdef NO_GEOMETRY_SHADER + drawId + #else + vsDrawId + #define drawId vsDrawId + #endif + = drawOffset + uint( + #ifndef GL_ES + gl_DrawIDARB /* Using GL_ARB_shader_draw_parameters, not GLSL 4.6 */ + #else + gl_DrawID + #endif + ); + #else + #define drawId drawOffset + #endif + #ifdef TWO_DIMENSIONS - highp const mat3 transformationProjectionMatrix = transformationProjectionMatrices[drawOffset]; + highp const mat3 transformationProjectionMatrix = transformationProjectionMatrices[drawId]; #elif defined(THREE_DIMENSIONS) - highp const mat4 transformationMatrix = transformationMatrices[drawOffset]; + highp const mat4 transformationMatrix = transformationMatrices[drawId]; #else #error #endif #if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(BITANGENT_FROM_TANGENT_DIRECTION) || defined(NORMAL_DIRECTION) - mediump const mat3 normalMatrix = draws[drawOffset].normalMatrix; + mediump const mat3 normalMatrix = draws[drawId].normalMatrix; #endif - mediump const uint materialId = draws[drawOffset].draw_materialIdReserved & 0xffffu; + mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; lowp float colorMapOffset = materials[materialId].material_colorMapOffset; lowp float colorMapScale = materials[materialId].material_colorMapScale; highp float lineLength = materials[materialId].material_lineLength; diff --git a/src/Magnum/Shaders/MeshVisualizerGL.cpp b/src/Magnum/Shaders/MeshVisualizerGL.cpp index 5c9488408..99cfa37f1 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.cpp +++ b/src/Magnum/Shaders/MeshVisualizerGL.cpp @@ -97,6 +97,17 @@ MeshVisualizerGLBase::MeshVisualizerGLBase(FlagsBase flags if(flags >= FlagBase::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= FlagBase::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_draw_parameters); + #elif !defined(MAGNUM_TARGET_WEBGL) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ANGLE::multi_draw); + #else + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::WEBGL::multi_draw); + #endif + } + #endif #ifndef MAGNUM_TARGET_GLES2 if(_flags & FlagBase::Wireframe && !(_flags & FlagBase::NoGeometryShader)) { @@ -169,6 +180,7 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra "#define MATERIAL_COUNT {}\n", _drawCount, _materialCount)); + vert.addSource(_flags >= FlagBase::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif frag.addSource(_flags & FlagBase::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") @@ -189,6 +201,7 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra "#define MATERIAL_COUNT {}\n", _drawCount, _materialCount)); + frag.addSource(_flags >= FlagBase::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif @@ -358,6 +371,7 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags "#define MATERIAL_COUNT {}\n", _drawCount, _materialCount)); + geom->addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif geom->addSource(rs.get("MeshVisualizer.geom")); @@ -673,6 +687,7 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags "#define MATERIAL_COUNT {}\n", _drawCount, _materialCount)); + geom->addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif geom->addSource(rs.get("MeshVisualizer.geom")); @@ -990,6 +1005,7 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flag value) { #endif #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + _c(MultiDraw) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -1022,6 +1038,7 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL3D::Flag value) { #endif #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + _c(MultiDraw) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -1044,6 +1061,7 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flags value) { MeshVisualizerGL2D::Flag::PrimitiveId, #endif #ifndef MAGNUM_TARGET_GLES2 + MeshVisualizerGL2D::Flag::MultiDraw, /* Superset of UniformBuffers */ MeshVisualizerGL2D::Flag::UniformBuffers #endif #endif @@ -1070,6 +1088,7 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL3D::Flags value) { MeshVisualizerGL3D::Flag::PrimitiveId, #endif #ifndef MAGNUM_TARGET_GLES2 + MeshVisualizerGL3D::Flag::MultiDraw, /* Superset of UniformBuffers */ MeshVisualizerGL3D::Flag::UniformBuffers #endif #endif diff --git a/src/Magnum/Shaders/MeshVisualizerGL.h b/src/Magnum/Shaders/MeshVisualizerGL.h index fa750c895..6e0df5a3c 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.h +++ b/src/Magnum/Shaders/MeshVisualizerGL.h @@ -55,7 +55,8 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgr PrimitiveId = 1 << 4, PrimitiveIdFromVertexId = (1 << 5)|PrimitiveId, /* bit 6, 7, 8, 9 used by 3D-specific TBN visualization */ - UniformBuffers = 1 << 10 + UniformBuffers = 1 << 10, + MultiDraw = UniformBuffers|(1 << 11) #endif }; typedef Containers::EnumSet FlagsBase; @@ -236,7 +237,31 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * 1.0. * @m_since_latest */ - UniformBuffers = 1 << 10 + UniformBuffers = 1 << 10, + + /** + * Enable multidraw functionality. Implies @ref Flag::UniformBuffers + * and combines the value from @ref setDrawOffset() with the + * @glsl gl_DrawID @ce builtin, which makes draws submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up per-draw parameters directly, without having to rebind + * the uniform buffers or specify @ref setDrawOffset() before each + * draw. In a non-multidraw scenario, @glsl gl_DrawID @ce is + * @cpp 0 @ce, which means a shader with this flag enabled can be + * used for regular draws as well. + * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} + * and @gl_extension{ARB,shader_draw_parameters} + * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). + * While the extension alone needs only OpenGL ES 2.0, the + * shader implementation relies on uniform buffers, which + * require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. + * While the extension alone needs only WebGL 1.0, the shader + * implementation relies on uniform buffers, which require + * WebGL 2.0. + * @m_since_latest + */ + MultiDraw = UniformBuffers|(1 << 11) #endif }; @@ -470,6 +495,11 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * should be used for current draw. Expects that * @ref Flag::UniformBuffers is set and @p offset is less than * @ref drawCount(). Initial value is @cpp 0 @ce. + * + * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this + * value, which makes each draw submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up its own per-draw parameters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. @@ -997,7 +1027,31 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * 1.0. * @m_since_latest */ - UniformBuffers = 1 << 10 + UniformBuffers = 1 << 10, + + /** + * Enable multidraw functionality. Implies @ref Flag::UniformBuffers + * and combines the value from @ref setDrawOffset() with the + * @glsl gl_DrawID @ce builtin, which makes draws submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up per-draw parameters directly, without having to rebind + * the uniform buffers or specify @ref setDrawOffset() before each + * draw. In a non-multidraw scenario, @glsl gl_DrawID @ce is + * @cpp 0 @ce, which means a shader with this flag enabled can be + * used for regular draws as well. + * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} + * and @gl_extension{ARB,shader_draw_parameters} + * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). + * While the extension alone needs only OpenGL ES 2.0, the + * shader implementation relies on uniform buffers, which + * require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. + * While the extension alone needs only WebGL 1.0, the shader + * implementation relies on uniform buffers, which require + * WebGL 2.0. + * @m_since_latest + */ + MultiDraw = UniformBuffers|(1 << 11) #endif }; @@ -1382,6 +1436,11 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * used for current draw. Expects that @ref Flag::UniformBuffers is set * and @p offset is less than @ref drawCount(). Initial value is * @cpp 0 @ce. + * + * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this + * value, which makes each draw submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up its own per-draw parameters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index 07c11a2fe..390e01e2f 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -148,6 +148,7 @@ uniform lowp float lightRanges[LIGHT_COUNT] /* Uniform buffers */ #else +#ifndef MULTI_DRAW #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -156,6 +157,8 @@ uniform highp uint drawOffset = 0u #endif ; +#define drawId drawOffset +#endif /* Keep in sync with Phong.vert. Can't "outsource" to a common file because the #extension directive needs to be always before any code. */ @@ -277,6 +280,10 @@ in lowp vec4 interpolatedVertexColor; flat in highp uint interpolatedInstanceObjectId; #endif +#ifdef MULTI_DRAW +flat in highp uint drawId; +#endif + /* Outputs */ #ifdef NEW_GLSL @@ -296,9 +303,9 @@ out highp uint fragmentObjectId; void main() { #ifdef UNIFORM_BUFFERS #ifdef OBJECT_ID - highp const uint objectId = draws[drawOffset].draw_objectId; + highp const uint objectId = draws[drawId].draw_objectId; #endif - mediump const uint materialId = draws[drawOffset].draw_materialIdReserved & 0xffffu; + mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; lowp const vec4 ambientColor = materials[materialId].ambientColor; #if LIGHT_COUNT lowp const vec4 diffuseColor = materials[materialId].diffuseColor; @@ -312,7 +319,7 @@ void main() { lowp const float alphaMask = materials[materialId].material_alphaMask; #endif #if LIGHT_COUNT - mediump const uint lightOffset = draws[drawOffset].draw_lightOffset; + mediump const uint lightOffset = draws[drawId].draw_lightOffset; #endif #endif @@ -370,7 +377,7 @@ void main() { #ifndef UNIFORM_BUFFERS for(int i = 0; i < LIGHT_COUNT; ++i) #else - for(uint i = 0u, actualLightCount = min(uint(LIGHT_COUNT), draws[drawOffset].draw_lightCount); i < actualLightCount; ++i) + for(uint i = 0u, actualLightCount = min(uint(LIGHT_COUNT), draws[drawId].draw_lightCount); i < actualLightCount; ++i) #endif { lowp const vec3 lightColor = diff --git a/src/Magnum/Shaders/Phong.vert b/src/Magnum/Shaders/Phong.vert index d4a4c5514..97f5920ba 100644 --- a/src/Magnum/Shaders/Phong.vert +++ b/src/Magnum/Shaders/Phong.vert @@ -27,6 +27,14 @@ #extension GL_EXT_gpu_shader4: require #endif +#ifdef MULTI_DRAW +#ifndef GL_ES +#extension GL_ARB_shader_draw_parameters: require +#else /* covers WebGL as well */ +#extension GL_ANGLE_multi_draw: require +#endif +#endif + #ifndef NEW_GLSL #define in attribute #define out varying @@ -273,17 +281,33 @@ out highp vec4 lightDirections[LIGHT_COUNT]; out highp vec3 cameraDirection; #endif +#ifdef MULTI_DRAW +flat out highp uint drawId; +#endif + void main() { #ifdef UNIFORM_BUFFERS - highp const mat4 transformationMatrix = transformationMatrices[drawOffset]; + #ifdef MULTI_DRAW + drawId = drawOffset + uint( + #ifndef GL_ES + gl_DrawIDARB /* Using GL_ARB_shader_draw_parameters, not GLSL 4.6 */ + #else + gl_DrawID + #endif + ); + #else + #define drawId drawOffset + #endif + + highp const mat4 transformationMatrix = transformationMatrices[drawId]; #if LIGHT_COUNT - mediump const mat3 normalMatrix = draws[drawOffset].normalMatrix; + mediump const mat3 normalMatrix = draws[drawId].normalMatrix; #endif #ifdef TEXTURE_TRANSFORMATION - mediump const mat3 textureMatrix = mat3(textureTransformations[drawOffset].rotationScaling.xy, 0.0, textureTransformations[drawOffset].rotationScaling.zw, 0.0, textureTransformations[drawOffset].textureTransformation_offset, 1.0); + mediump const mat3 textureMatrix = mat3(textureTransformations[drawId].rotationScaling.xy, 0.0, textureTransformations[drawId].rotationScaling.zw, 0.0, textureTransformations[drawId].textureTransformation_offset, 1.0); #endif #if LIGHT_COUNT - mediump const uint lightOffset = draws[drawOffset].draw_lightOffset; + mediump const uint lightOffset = draws[drawId].draw_lightOffset; #endif #endif @@ -328,7 +352,7 @@ void main() { #ifndef UNIFORM_BUFFERS for(int i = 0; i < LIGHT_COUNT; ++i) #else - for(uint i = 0u, actualLightCount = min(uint(LIGHT_COUNT), draws[drawOffset].draw_lightCount); i < actualLightCount; ++i) + for(uint i = 0u, actualLightCount = min(uint(LIGHT_COUNT), draws[drawId].draw_lightCount); i < actualLightCount; ++i) #endif { highp const vec4 lightPosition = diff --git a/src/Magnum/Shaders/PhongGL.cpp b/src/Magnum/Shaders/PhongGL.cpp index 00e1083da..377b5443b 100644 --- a/src/Magnum/Shaders/PhongGL.cpp +++ b/src/Magnum/Shaders/PhongGL.cpp @@ -105,6 +105,17 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount if(flags >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_draw_parameters); + #elif !defined(MAGNUM_TARGET_WEBGL) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ANGLE::multi_draw); + #else + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::WEBGL::multi_draw); + #endif + } + #endif #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ @@ -199,6 +210,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount "#define LIGHT_COUNT {}\n", drawCount, lightCount)); + vert.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif #ifndef MAGNUM_TARGET_GLES @@ -229,6 +241,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount drawCount, materialCount, lightCount)); + frag.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } else #endif { @@ -809,6 +822,7 @@ Debug& operator<<(Debug& debug, const PhongGL::Flag value) { _c(InstancedTextureOffset) #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + _c(MultiDraw) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -834,6 +848,7 @@ Debug& operator<<(Debug& debug, const PhongGL::Flags value) { #endif PhongGL::Flag::InstancedTransformation, #ifndef MAGNUM_TARGET_GLES2 + PhongGL::Flag::MultiDraw, /* Superset of UniformBuffers */ PhongGL::Flag::UniformBuffers #endif }); diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index cb67a8a02..7e949613c 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -577,7 +577,31 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * 1.0. * @m_since_latest */ - UniformBuffers = 1 << 12 + UniformBuffers = 1 << 12, + + /** + * Enable multidraw functionality. Implies @ref Flag::UniformBuffers + * and adds the value from @ref setDrawOffset() with the + * @glsl gl_DrawID @ce builtin, which makes draws submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up per-draw parameters directly, without having to rebind + * the uniform buffers or specify @ref setDrawOffset() before each + * draw. In a non-multidraw scenario, @glsl gl_DrawID @ce is + * @cpp 0 @ce, which means a shader with this flag enabled can be + * used for regular draws as well. + * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} + * and @gl_extension{ARB,shader_draw_parameters} + * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). + * While the extension alone needs only OpenGL ES 2.0, the + * shader implementation relies on uniform buffers, which + * require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. + * While the extension alone needs only WebGL 1.0, the shader + * implementation relies on uniform buffers, which require + * WebGL 2.0. + * @m_since_latest + */ + MultiDraw = UniformBuffers|(1 << 13) #endif }; @@ -1162,6 +1186,11 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * and @ref bindTextureTransformationBuffer() should be used for * current draw. Expects that @ref Flag::UniformBuffers is set and * @p offset is less than @ref drawCount(). Initial value is @cpp 0 @ce. + * + * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this + * value, which makes each draw submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up its own per-draw parameters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp index 055cde641..518286fcb 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp @@ -129,24 +129,27 @@ struct DistanceFieldVectorGLTest: GL::OpenGLTester { Rendering tests done: [B] base - [O] draw offset + [O] UBOs + draw offset + [M] multidraw - Mesa Intel BO - ES2 x - ES3 BO + Mesa Intel BOM + ES2 xx + ES3 BOx Mesa AMD B Mesa llvmpipe B - SwiftShader ES2 Bx + SwiftShader ES2 Bxx ES3 B - ARM Mali (Huawei P10) ES2 Bx - ES3 BO - WebGL (on Mesa Intel) 1.0 Bx - 2.0 BO + ANGLE ES2 xx + ES3 BOM + ARM Mali (Huawei P10) ES2 Bxx + ES3 BOx + WebGL (on Mesa Intel) 1.0 Bxx + 2.0 BOM NVidia Intel Windows - AMD macOS - Intel macOS BO - iPhone 6 w/ iOS 12.4 ES3 B + AMD macOS x + Intel macOS BOx + iPhone 6 w/ iOS 12.4 ES3 B x */ using namespace Math::Literals; @@ -171,6 +174,7 @@ constexpr struct { /* SwiftShader has 256 uniform vectors at most, per-draw is 4+1 in 3D case and 3+1 in 2D, per-material 4 */ {"multiple materials, draws", DistanceFieldVectorGL2D::Flag::UniformBuffers, 16, 48}, + {"multidraw with all the things", DistanceFieldVectorGL2D::Flag::MultiDraw|DistanceFieldVectorGL2D::Flag::TextureTransformation, 16, 48} }; constexpr struct { @@ -213,16 +217,21 @@ constexpr struct { const char* name; const char* expected2D; const char* expected3D; + DistanceFieldVectorGL2D::Flags flags; UnsignedInt materialCount, drawCount; UnsignedInt uniformIncrement; Float maxThreshold, meanThreshold; } RenderMultiData[] { {"bind with offset", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", - 1, 1, 16, + {}, 1, 1, 16, /* Minor differences on ARM Mali */ 1.67f, 0.012f}, {"draw offset", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", - 2, 3, 1, + {}, 2, 3, 1, + /* Minor differences on ARM Mali */ + 1.67f, 0.012f}, + {"multidraw", "multidraw2D-distancefield.tga", "multidraw3D-distancefield.tga", + DistanceFieldVectorGL2D::Flag::MultiDraw, 2, 3, 1, /* Minor differences on ARM Mali */ 1.67f, 0.012f}, }; @@ -369,6 +378,19 @@ template void DistanceFieldVectorGLTest::constructUnifor CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= DistanceFieldVectorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + DistanceFieldVectorGL shader{data.flags, data.materialCount, data.drawCount}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_COMPARE(shader.materialCount(), data.materialCount); @@ -1036,6 +1058,19 @@ void DistanceFieldVectorGLTest::renderMulti2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= DistanceFieldVectorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::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."); @@ -1131,7 +1166,7 @@ void DistanceFieldVectorGLTest::renderMulti2D() { .setMaterialId(data.drawCount == 1 ? 0 : 0); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - DistanceFieldVectorGL2D shader{DistanceFieldVectorGL2D::Flag::UniformBuffers|DistanceFieldVectorGL2D::Flag::TextureTransformation, data.materialCount, data.drawCount}; + DistanceFieldVectorGL2D shader{DistanceFieldVectorGL2D::Flag::UniformBuffers|DistanceFieldVectorGL2D::Flag::TextureTransformation|data.flags, data.materialCount, data.drawCount}; shader.bindVectorTexture(vector); /* Just one draw, rebinding UBOs each time */ @@ -1178,18 +1213,23 @@ void DistanceFieldVectorGLTest::renderMulti2D() { sizeof(TextureTransformationUniform)); shader.draw(triangle); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) .bindMaterialBuffer(materialUniform) .bindTextureTransformationBuffer(textureTransformationUniform); - shader.setDrawOffset(0) - .draw(circle); - shader.setDrawOffset(1) - .draw(square); - shader.setDrawOffset(2) - .draw(triangle); + + if(data.flags >= DistanceFieldVectorGL2D::Flag::MultiDraw) + shader.draw({circle, square, triangle}); + else { + shader.setDrawOffset(0) + .draw(circle); + shader.setDrawOffset(1) + .draw(square); + shader.setDrawOffset(2) + .draw(triangle); + } } /* @@ -1214,6 +1254,19 @@ void DistanceFieldVectorGLTest::renderMulti3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= DistanceFieldVectorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::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."); @@ -1314,7 +1367,7 @@ void DistanceFieldVectorGLTest::renderMulti3D() { .setMaterialId(data.drawCount == 1 ? 0 : 0); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - DistanceFieldVectorGL3D shader{DistanceFieldVectorGL3D::Flag::UniformBuffers|DistanceFieldVectorGL3D::Flag::TextureTransformation, data.materialCount, data.drawCount}; + DistanceFieldVectorGL3D shader{DistanceFieldVectorGL3D::Flag::UniformBuffers|DistanceFieldVectorGL3D::Flag::TextureTransformation|data.flags, data.materialCount, data.drawCount}; shader.bindVectorTexture(vector); /* Just one draw, rebinding UBOs each time */ @@ -1361,18 +1414,23 @@ void DistanceFieldVectorGLTest::renderMulti3D() { sizeof(TextureTransformationUniform)); shader.draw(cone); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) .bindMaterialBuffer(materialUniform) .bindTextureTransformationBuffer(textureTransformationUniform); - shader.setDrawOffset(0) - .draw(sphere); - shader.setDrawOffset(1) - .draw(plane); - shader.setDrawOffset(2) - .draw(cone); + + if(data.flags >= DistanceFieldVectorGL3D::Flag::MultiDraw) + shader.draw({sphere, plane, cone}); + else { + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } } /* diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGL_Test.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGL_Test.cpp index b337b24c5..c9e206ae4 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGL_Test.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGL_Test.cpp @@ -41,17 +41,25 @@ struct DistanceFieldVectorGL_Test: TestSuite::Tester { void debugFlag(); void debugFlags(); + #ifndef MAGNUM_TARGET_GLES2 + void debugFlagsSupersets(); + #endif }; DistanceFieldVectorGL_Test::DistanceFieldVectorGL_Test() { - addTests({&DistanceFieldVectorGL_Test::constructNoCreate<2>, - &DistanceFieldVectorGL_Test::constructNoCreate<3>, - - &DistanceFieldVectorGL_Test::constructCopy<2>, - &DistanceFieldVectorGL_Test::constructCopy<3>, - - &DistanceFieldVectorGL_Test::debugFlag, - &DistanceFieldVectorGL_Test::debugFlags}); + addTests({ + &DistanceFieldVectorGL_Test::constructNoCreate<2>, + &DistanceFieldVectorGL_Test::constructNoCreate<3>, + + &DistanceFieldVectorGL_Test::constructCopy<2>, + &DistanceFieldVectorGL_Test::constructCopy<3>, + + &DistanceFieldVectorGL_Test::debugFlag, + &DistanceFieldVectorGL_Test::debugFlags, + #ifndef MAGNUM_TARGET_GLES2 + &DistanceFieldVectorGL_Test::debugFlagsSupersets + #endif + }); } template void DistanceFieldVectorGL_Test::constructNoCreate() { @@ -87,6 +95,15 @@ void DistanceFieldVectorGL_Test::debugFlags() { CORRADE_COMPARE(out.str(), "Shaders::DistanceFieldVectorGL::Flag::TextureTransformation|Shaders::DistanceFieldVectorGL::Flag(0xf0) Shaders::DistanceFieldVectorGL::Flags{}\n"); } +#ifndef MAGNUM_TARGET_GLES2 +void DistanceFieldVectorGL_Test::debugFlagsSupersets() { + /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + std::ostringstream out; + Debug{&out} << (DistanceFieldVectorGL3D::Flag::MultiDraw|DistanceFieldVectorGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::DistanceFieldVectorGL::Flag::MultiDraw\n"); +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::DistanceFieldVectorGL_Test) diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index 6d5d4f093..974365108 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -161,24 +161,27 @@ struct FlatGLTest: GL::OpenGLTester { [A] alpha mask [D] object ID [I] instancing - [O] draw offset + [O] UBOs + draw offset + [M] multidraw - Mesa Intel BADIO - ES2 x - ES3 BADIO + Mesa Intel BADIOM + ES2 xx + ES3 BADIOx Mesa AMD BADI Mesa llvmpipe BADI - SwiftShader ES2 BADIx + SwiftShader ES2 BADIxx ES3 BADI - ARM Mali (Huawei P10) ES2 BAD x - ES3 BADIO - WebGL (on Mesa Intel) 1.0 BAD x - 2.0 BADIO + ANGLE ES2 xx + ES3 BADIOM + ARM Mali (Huawei P10) ES2 BAD xx + ES3 BADIOx + WebGL (on Mesa Intel) 1.0 BAD xx + 2.0 BADIOM NVidia BAD Intel Windows BAD AMD macOS BAD - Intel macOS BADIO - iPhone 6 w/ iOS 12.4 ES3 BAD + Intel macOS BADIOx + iPhone 6 w/ iOS 12.4 ES3 BAD x */ using namespace Math::Literals; @@ -216,7 +219,8 @@ constexpr struct { {"multiple draws", FlatGL2D::Flag::UniformBuffers, 42}, {"texture transformation", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1}, {"alpha mask", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::AlphaMask, 1}, - {"object ID", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::ObjectId, 1} + {"object ID", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::ObjectId, 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, 42} }; #endif @@ -290,11 +294,19 @@ constexpr struct { /* Minor differences on ARM Mali */ 2.34f, 0.01f}, {"draw offset, colored", "multidraw2D.tga", "multidraw3D.tga", - {}, 3, 1, 0.0f, 0.0f}, + {}, + 3, 1, 0.0f, 0.0f}, {"draw offset, textured", "multidraw-textured2D.tga", "multidraw-textured3D.tga", FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured, 3, 1, /* Minor differences on ARM Mali */ + 2.34f, 0.01f}, + {"multidraw, colored", "multidraw2D.tga", "multidraw3D.tga", + FlatGL2D::Flag::MultiDraw, 3, 1, 0.0f, 0.0f}, + {"multidraw, textured", "multidraw-textured2D.tga", "multidraw-textured3D.tga", + FlatGL2D::Flag::MultiDraw|FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured, + 3, 1, + /* Minor differences on ARM Mali */ 2.34f, 0.01f} }; #endif @@ -532,6 +544,19 @@ template void FlatGLTest::constructUniformBuffers() { CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + if(data.flags >= FlatGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + FlatGL shader{data.flags, data.drawCount}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_COMPARE(shader.drawCount(), data.drawCount); @@ -2266,6 +2291,19 @@ void FlatGLTest::renderMulti2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= FlatGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + GL::Texture2D texture; if(data.flags & FlatGL2D::Flag::Textured) { if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || @@ -2402,18 +2440,23 @@ void FlatGLTest::renderMulti2D() { sizeof(TextureTransformationUniform)); shader.draw(triangle); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform); if(data.flags & FlatGL2D::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); - shader.setDrawOffset(0) - .draw(circle); - shader.setDrawOffset(1) - .draw(square); - shader.setDrawOffset(2) - .draw(triangle); + + if(data.flags >= FlatGL2D::Flag::MultiDraw) + shader.draw({circle, square, triangle}); + else { + shader.setDrawOffset(0) + .draw(circle); + shader.setDrawOffset(1) + .draw(square); + shader.setDrawOffset(2) + .draw(triangle); + } } MAGNUM_VERIFY_NO_GL_ERROR(); @@ -2469,6 +2512,19 @@ void FlatGLTest::renderMulti3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= FlatGL3D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + GL::Texture2D texture; if(data.flags & FlatGL3D::Flag::Textured) { if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || @@ -2611,18 +2667,23 @@ void FlatGLTest::renderMulti3D() { sizeof(TextureTransformationUniform)); shader.draw(cone); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform); if(data.flags & FlatGL3D::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); - shader.setDrawOffset(0) - .draw(sphere); - shader.setDrawOffset(1) - .draw(plane); - shader.setDrawOffset(2) - .draw(cone); + + if(data.flags >= FlatGL3D::Flag::MultiDraw) + shader.draw({sphere, plane, cone}); + else { + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } } MAGNUM_VERIFY_NO_GL_ERROR(); diff --git a/src/Magnum/Shaders/Test/FlatGL_Test.cpp b/src/Magnum/Shaders/Test/FlatGL_Test.cpp index 640e18d78..f4e9c183c 100644 --- a/src/Magnum/Shaders/Test/FlatGL_Test.cpp +++ b/src/Magnum/Shaders/Test/FlatGL_Test.cpp @@ -102,9 +102,20 @@ void FlatGL_Test::debugFlagsSupersets() { /* InstancedTextureOffset is a superset of TextureTransformation so only one should be printed */ - std::ostringstream out; - Debug{&out} << (FlatGL3D::Flag::InstancedTextureOffset|FlatGL3D::Flag::TextureTransformation); - CORRADE_COMPARE(out.str(), "Shaders::FlatGL::Flag::InstancedTextureOffset\n"); + { + std::ostringstream out; + Debug{&out} << (FlatGL3D::Flag::InstancedTextureOffset|FlatGL3D::Flag::TextureTransformation); + CORRADE_COMPARE(out.str(), "Shaders::FlatGL::Flag::InstancedTextureOffset\n"); + } + + #ifndef MAGNUM_TARGET_GLES2 + /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + { + std::ostringstream out; + Debug{&out} << (FlatGL3D::Flag::MultiDraw|FlatGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::FlatGL::Flag::MultiDraw\n"); + } + #endif } }}}} diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index 0b6bdbd92..9c13ebba6 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -180,23 +180,26 @@ struct MeshVisualizerGLTest: GL::OpenGLTester { [D] primitive/vertex/object ID [T] TBN visualization [O] draw offset + [M] multidraw - Mesa Intel WDTO - ES2 x - ES3 + Mesa Intel WDTOM + ES2 xx + ES3 x Mesa AMD WDT Mesa llvmpipe WDT - SwiftShader ES2 WDxx + SwiftShader ES2 WDxxx ES3 WDx - ARM Mali (Huawei P10) ES2 W xx - ES3 W O (WDT big diffs, needs investigation) - WebGL (on Mesa Intel) 1.0 W xx - 2.0 W x + ANGLE ES2 xx + ES3 WDxOM + ARM Mali (Huawei P10) ES2 W xxx + ES3 W Ox (WDT big diffs, needs investigation) + WebGL (on Mesa Intel) 1.0 W xxx + 2.0 W x M NVidia Intel Windows AMD macOS - Intel macOS WDTO - iPhone 6 w/ iOS 12.4 ES3 W x + Intel macOS WDTOx + iPhone 6 w/ iOS 12.4 ES3 W x x */ using namespace Math::Literals; @@ -232,6 +235,10 @@ constexpr struct { /* SwiftShader has 256 uniform vectors at most, per-2D-draw is 4, per-material 4, two need to be left for drawOffset + viewportSize */ {"multiple materials, draws", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 8, 55}, + {"multidraw with wireframe w/o GS and vertex ID", MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader|MeshVisualizerGL2D::Flag::VertexId, 8, 55}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw with wireframe and primitive ID", MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::PrimitiveId, 8, 55}, + #endif /* The rest is basically a copy of ConstructData2D with UniformBuffers added */ #ifndef MAGNUM_TARGET_WEBGL @@ -293,6 +300,10 @@ constexpr struct { /* SwiftShader has 256 uniform vectors at most, per-3D-draw is 4+4, per-material 4, plus 4 for projection */ {"multiple materials, draws", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 6, 28}, + {"multidraw with wireframe w/o GS and vertex ID", MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader|MeshVisualizerGL3D::Flag::VertexId, 6, 28}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw with wireframe, primitive ID and TBN", MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::PrimitiveId|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentDirection|MeshVisualizerGL3D::Flag::NormalDirection, 6, 28}, + #endif /* The rest is basically a copy of ConstructData2D with UniformBuffers added */ #ifndef MAGNUM_TARGET_WEBGL @@ -605,6 +616,23 @@ constexpr struct { 2, 3, 1, /* Minor differences on ARM Mali */ 0.67f, 0.01f}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, wireframe", "multidraw-wireframe2D.tga", + MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::Wireframe, + 2, 3, 1, + /* Minor differences on ARM Mali */ + 0.67f, 0.01f}, + #endif + {"multidraw, wireframe w/o GS", "multidraw-wireframe-nogeo2D.tga", + MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, + 2, 3, 1, + /* Minor differences on ARM Mali */ + 0.67f, 0.02f}, + {"multidraw, vertex ID", "multidraw-vertexid2D.tga", + MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::VertexId, + 2, 3, 1, + /* Minor differences on ARM Mali */ + 0.67f, 0.01f}, }; constexpr struct { @@ -651,6 +679,24 @@ constexpr struct { 2, 3, 1, /* Minor differences on ARM Mali */ 0.67f, 0.01f}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, wireframe", "multidraw-wireframe3D.tga", + MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe, + 2, 3, 1, 0.0f, 0.0f}, + {"multidraw, wireframe + TBN", "multidraw-wireframe-tbn3D.tga", + MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::TangentDirection|MeshVisualizerGL3D::Flag::BitangentFromTangentDirection|MeshVisualizerGL3D::Flag::NormalDirection, + 2, 3, 1, 0.0f, 0.0f}, + #endif + {"multidraw, wireframe w/o GS", "multidraw-wireframe-nogeo3D.tga", + MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, + 2, 3, 1, + /* Minor differences on ARM Mali */ + 6.0f, 0.04f}, + {"multidraw, vertex ID", "multidraw-vertexid3D.tga", + MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::VertexId, + 2, 3, 1, + /* Minor differences on ARM Mali */ + 0.67f, 0.01f}, }; #endif @@ -998,6 +1044,19 @@ void MeshVisualizerGLTest::constructUniformBuffers2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= MeshVisualizerGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + MeshVisualizerGL2D shader{data.flags, data.materialCount, data.drawCount}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_VERIFY(shader.id()); @@ -1123,6 +1182,19 @@ void MeshVisualizerGLTest::constructUniformBuffers3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= MeshVisualizerGL3D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + MeshVisualizerGL3D shader{data.flags, data.materialCount, data.drawCount}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_VERIFY(shader.id()); @@ -3158,6 +3230,19 @@ void MeshVisualizerGLTest::renderMulti2D() { } #endif + if(data.flags >= MeshVisualizerGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + /* Circle is a fan, plane is a strip, make it indexed first */ Trade::MeshData circleData = MeshTools::generateIndices(Primitives::circle2DSolid(8)); Trade::MeshData squareData = MeshTools::generateIndices(Primitives::squareSolid()); @@ -3273,17 +3358,22 @@ void MeshVisualizerGLTest::renderMulti2D() { sizeof(MeshVisualizerDrawUniform2D)); shader.draw(triangle); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindMaterialBuffer(materialUniform) .bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform); - shader.setDrawOffset(0) - .draw(circle); - shader.setDrawOffset(1) - .draw(square); - shader.setDrawOffset(2) - .draw(triangle); + + if(data.flags >= MeshVisualizerGL2D::Flag::MultiDraw) + shader.draw({circle, square, triangle}); + else { + shader.setDrawOffset(0) + .draw(circle); + shader.setDrawOffset(1) + .draw(square); + shader.setDrawOffset(2) + .draw(triangle); + } }; MAGNUM_VERIFY_NO_GL_ERROR(); @@ -3333,6 +3423,19 @@ void MeshVisualizerGLTest::renderMulti3D() { } #endif + if(data.flags >= MeshVisualizerGL3D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + Trade::MeshData sphereData = MeshTools::interleave(Primitives::icosphereSolid(0), { /* The icosphere doesn't have tangents and we don't use them, but concatenate() will ignore the tangents of others if the first mesh @@ -3462,17 +3565,22 @@ void MeshVisualizerGLTest::renderMulti3D() { sizeof(MeshVisualizerDrawUniform3D)); shader.draw(cone); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindMaterialBuffer(materialUniform) .bindTransformationBuffer(transformationUniform) .bindDrawBuffer(drawUniform); - shader.setDrawOffset(0) - .draw(sphere); - shader.setDrawOffset(1) - .draw(plane); - shader.setDrawOffset(2) - .draw(cone); + + if(data.flags >= MeshVisualizerGL3D::Flag::MultiDraw) + shader.draw({sphere, plane, cone}); + else { + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } }; MAGNUM_VERIFY_NO_GL_ERROR(); diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp index acee35225..10e7ffc53 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp @@ -48,21 +48,31 @@ struct MeshVisualizerGL_Test: TestSuite::Tester { void debugFlag3D(); void debugFlags2D(); void debugFlags3D(); + #ifndef MAGNUM_TARGET_GLES2 + void debugFlagsSupersets2D(); + void debugFlagsSupersets3D(); + #endif }; MeshVisualizerGL_Test::MeshVisualizerGL_Test() { - addTests({&MeshVisualizerGL_Test::constructNoCreate2D, - &MeshVisualizerGL_Test::constructNoCreate3D, - - &MeshVisualizerGL_Test::constructCopy2D, - &MeshVisualizerGL_Test::constructCopy3D, - - &MeshVisualizerGL_Test::vertexIndexSameAsObjectId, - - &MeshVisualizerGL_Test::debugFlag2D, - &MeshVisualizerGL_Test::debugFlag3D, - &MeshVisualizerGL_Test::debugFlags2D, - &MeshVisualizerGL_Test::debugFlags3D}); + addTests({ + &MeshVisualizerGL_Test::constructNoCreate2D, + &MeshVisualizerGL_Test::constructNoCreate3D, + + &MeshVisualizerGL_Test::constructCopy2D, + &MeshVisualizerGL_Test::constructCopy3D, + + &MeshVisualizerGL_Test::vertexIndexSameAsObjectId, + + &MeshVisualizerGL_Test::debugFlag2D, + &MeshVisualizerGL_Test::debugFlag3D, + &MeshVisualizerGL_Test::debugFlags2D, + &MeshVisualizerGL_Test::debugFlags3D, + #ifndef MAGNUM_TARGET_GLES2 + &MeshVisualizerGL_Test::debugFlagsSupersets2D, + &MeshVisualizerGL_Test::debugFlagsSupersets3D, + #endif + }); } void MeshVisualizerGL_Test::constructNoCreate2D() { @@ -140,6 +150,22 @@ void MeshVisualizerGL_Test::debugFlags3D() { #endif } +#ifndef MAGNUM_TARGET_GLES2 +void MeshVisualizerGL_Test::debugFlagsSupersets2D() { + /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + std::ostringstream out; + Debug{&out} << (MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL2D::Flag::MultiDraw\n"); +} + +void MeshVisualizerGL_Test::debugFlagsSupersets3D() { + /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + std::ostringstream out; + Debug{&out} << (MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::MeshVisualizerGL3D::Flag::MultiDraw\n"); +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::MeshVisualizerGL_Test) diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 4a952bce2..ce99031f0 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -168,24 +168,27 @@ struct PhongGLTest: GL::OpenGLTester { [D] object ID [L] point lights [I] instancing - [O] draw offset + [O] UBOs + draw offset + [M] multidraw - Mesa Intel BADLIO - ES2 x - ES3 BADLIO + Mesa Intel BADLIOM + ES2 xx + ES3 BADLIOx Mesa AMD BAD I Mesa llvmpipe BAD I - SwiftShader ES2 BADLIx + SwiftShader ES2 BADLIxx ES3 BADLI - ARM Mali (Huawei P10) ES2 BAD x - ES3 BADLIO - WebGL (on Mesa Intel) 1.0 BAD x - 2.0 BADLIO + ANGLE ES2 xx + ES3 BADLIOM + ARM Mali (Huawei P10) ES2 BAD xx + ES3 BADLIOx + WebGL (on Mesa Intel) 1.0 BAD xx + 2.0 BADLIOM NVidia BAD Intel Windows BAD AMD macOS BAD - Intel macOS BADLIO - iPhone 6 w/ iOS 12.4 ES3 BAD + Intel macOS BADLIOx + iPhone 6 w/ iOS 12.4 ES3 BAD x */ constexpr struct { @@ -244,7 +247,8 @@ constexpr struct { {"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} + {"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} }; #endif @@ -617,6 +621,16 @@ constexpr struct { 4, 2, 3, 1, /* Minor differences on ARM Mali */ 4.67f, 0.02f}, + {"multidraw, colored", "multidraw.tga", + PhongGL::Flag::MultiDraw, + 4, 2, 3, 1, + /* Minor differences on ARM Mali */ + 3.34f, 0.01f}, + {"multidraw, textured", "multidraw-textured.tga", + PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture, + 4, 2, 3, 1, + /* Minor differences on ARM Mali */ + 4.67f, 0.02f}, }; #endif @@ -875,6 +889,19 @@ void PhongGLTest::constructUniformBuffers() { CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); #endif + if(data.flags >= PhongGL::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + PhongGL shader{data.flags, data.lightCount, data.materialCount, data.drawCount}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_COMPARE(shader.lightCount(), data.lightCount); @@ -2971,6 +2998,19 @@ void PhongGLTest::renderMulti() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= PhongGL::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + GL::Texture2D diffuse; if(data.flags & PhongGL::Flag::DiffuseTexture) { if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || @@ -3176,7 +3216,7 @@ void PhongGLTest::renderMulti() { sizeof(TextureTransformationUniform)); shader.draw(cone); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationBuffer(transformationUniform) .bindDrawBuffer(drawUniform) @@ -3184,12 +3224,17 @@ void PhongGLTest::renderMulti() { .bindLightBuffer(lightUniform); if(data.flags & PhongGL::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); - shader.setDrawOffset(0) - .draw(sphere); - shader.setDrawOffset(1) - .draw(plane); - shader.setDrawOffset(2) - .draw(cone); + + if(data.flags >= PhongGL::Flag::MultiDraw) + shader.draw({sphere, plane, cone}); + else { + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } } /* diff --git a/src/Magnum/Shaders/Test/PhongGL_Test.cpp b/src/Magnum/Shaders/Test/PhongGL_Test.cpp index b76638f6c..b00618d21 100644 --- a/src/Magnum/Shaders/Test/PhongGL_Test.cpp +++ b/src/Magnum/Shaders/Test/PhongGL_Test.cpp @@ -96,9 +96,20 @@ void PhongGL_Test::debugFlagsSupersets() { /* InstancedTextureOffset is a superset of TextureTransformation so only one should be printed */ - std::ostringstream out; - Debug{&out} << (PhongGL::Flag::InstancedTextureOffset|PhongGL::Flag::TextureTransformation); - CORRADE_COMPARE(out.str(), "Shaders::PhongGL::Flag::InstancedTextureOffset\n"); + { + std::ostringstream out; + Debug{&out} << (PhongGL::Flag::InstancedTextureOffset|PhongGL::Flag::TextureTransformation); + CORRADE_COMPARE(out.str(), "Shaders::PhongGL::Flag::InstancedTextureOffset\n"); + } + + #ifndef MAGNUM_TARGET_GLES2 + /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + { + std::ostringstream out; + Debug{&out} << (PhongGL::Flag::MultiDraw|PhongGL::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::PhongGL::Flag::MultiDraw\n"); + } + #endif } }}}} diff --git a/src/Magnum/Shaders/Test/VectorGLTest.cpp b/src/Magnum/Shaders/Test/VectorGLTest.cpp index 04ba121a9..1f5260c95 100644 --- a/src/Magnum/Shaders/Test/VectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VectorGLTest.cpp @@ -128,24 +128,27 @@ struct VectorGLTest: GL::OpenGLTester { Rendering tests done: [B] base - [O] draw offset + [O] UBOs + draw offset + [M] multidraw - Mesa Intel BO - ES2 x - ES3 BO + Mesa Intel BOM + ES2 xx + ES3 BOx Mesa AMD B Mesa llvmpipe B - SwiftShader ES2 Bx + SwiftShader ES2 Bxx ES3 B - ARM Mali (Huawei P10) ES2 Bx - ES3 BO - WebGL (on Mesa Intel) 1.0 Bx - 2.0 BO + ANGLE ES2 xx + ES3 BOM + ARM Mali (Huawei P10) ES2 Bxx + ES3 BOx + WebGL (on Mesa Intel) 1.0 Bxx + 2.0 BOM NVidia Intel Windows - AMD macOS - Intel macOS BO - iPhone 6 w/ iOS 12.4 ES3 B + AMD macOS x + Intel macOS BOx + iPhone 6 w/ iOS 12.4 ES3 B x */ using namespace Math::Literals; @@ -170,6 +173,7 @@ constexpr struct { /* SwiftShader has 256 uniform vectors at most, per-draw is 4+3 in 3D case and 3+3 in 2D */ {"multiple draws", VectorGL2D::Flag::UniformBuffers, 36}, + {"multidraw with all the things", VectorGL2D::Flag::MultiDraw|VectorGL2D::Flag::TextureTransformation, 36} }; #endif @@ -195,16 +199,21 @@ constexpr struct { const char* name; const char* expected2D; const char* expected3D; + VectorGL2D::Flags flags; UnsignedInt drawCount; UnsignedInt uniformIncrement; Float maxThreshold, meanThreshold; } RenderMultiData[] { {"bind with offset", "multidraw2D.tga", "multidraw3D.tga", - 1, 16, + {}, 1, 16, /* Minor differences on ARM Mali */ 1.34f, 0.02f}, {"draw offset", "multidraw2D.tga", "multidraw3D.tga", - 3, 1, + {}, 3, 1, + /* Minor differences on ARM Mali */ + 1.34f, 0.02f}, + {"multidraw", "multidraw2D.tga", "multidraw3D.tga", + VectorGL2D::Flag::MultiDraw, 3, 1, /* Minor differences on ARM Mali */ 1.34f, 0.02f}, }; @@ -347,6 +356,19 @@ template void VectorGLTest::constructUniformBuffers() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= VectorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + VectorGL shader{data.flags, data.drawCount}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_COMPARE(shader.drawCount(), data.drawCount); @@ -958,6 +980,19 @@ void VectorGLTest::renderMulti2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= VectorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::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."); @@ -1045,7 +1080,7 @@ void VectorGLTest::renderMulti2D() { .setBackgroundColor(0xccffcc_rgbf); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - VectorGL2D shader{VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, data.drawCount}; + VectorGL2D shader{VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation|data.flags, data.drawCount}; shader.bindVectorTexture(vector); /* Just one draw, rebinding UBOs each time */ @@ -1083,17 +1118,22 @@ void VectorGLTest::renderMulti2D() { sizeof(TextureTransformationUniform)); shader.draw(triangle); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) .bindTextureTransformationBuffer(textureTransformationUniform); - shader.setDrawOffset(0) - .draw(circle); - shader.setDrawOffset(1) - .draw(square); - shader.setDrawOffset(2) - .draw(triangle); + + if(data.flags >= VectorGL2D::Flag::MultiDraw) + shader.draw({circle, square, triangle}); + else { + shader.setDrawOffset(0) + .draw(circle); + shader.setDrawOffset(1) + .draw(square); + shader.setDrawOffset(2) + .draw(triangle); + } } /* @@ -1118,6 +1158,19 @@ void VectorGLTest::renderMulti3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= VectorGL3D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::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."); @@ -1210,7 +1263,7 @@ void VectorGLTest::renderMulti3D() { .setBackgroundColor(0xccffcc_rgbf); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - VectorGL3D shader{VectorGL3D::Flag::UniformBuffers|VectorGL3D::Flag::TextureTransformation, data.drawCount}; + VectorGL3D shader{VectorGL3D::Flag::UniformBuffers|VectorGL3D::Flag::TextureTransformation|data.flags, data.drawCount}; shader.bindVectorTexture(vector); /* Just one draw, rebinding UBOs each time */ @@ -1248,17 +1301,22 @@ void VectorGLTest::renderMulti3D() { sizeof(TextureTransformationUniform)); shader.draw(cone); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) .bindTextureTransformationBuffer(textureTransformationUniform); - shader.setDrawOffset(0) - .draw(sphere); - shader.setDrawOffset(1) - .draw(plane); - shader.setDrawOffset(2) - .draw(cone); + + if(data.flags >= VectorGL3D::Flag::MultiDraw) + shader.draw({sphere, plane, cone}); + else { + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } } /* diff --git a/src/Magnum/Shaders/Test/VectorGL_Test.cpp b/src/Magnum/Shaders/Test/VectorGL_Test.cpp index ff8e7f2e6..f050f270d 100644 --- a/src/Magnum/Shaders/Test/VectorGL_Test.cpp +++ b/src/Magnum/Shaders/Test/VectorGL_Test.cpp @@ -41,17 +41,25 @@ struct VectorGL_Test: TestSuite::Tester { void debugFlag(); void debugFlags(); + #ifndef MAGNUM_TARGET_GLES2 + void debugFlagsSupersets(); + #endif }; VectorGL_Test::VectorGL_Test() { - addTests({&VectorGL_Test::constructNoCreate<2>, - &VectorGL_Test::constructNoCreate<3>, - - &VectorGL_Test::constructCopy<2>, - &VectorGL_Test::constructCopy<3>, - - &VectorGL_Test::debugFlag, - &VectorGL_Test::debugFlags}); + addTests({ + &VectorGL_Test::constructNoCreate<2>, + &VectorGL_Test::constructNoCreate<3>, + + &VectorGL_Test::constructCopy<2>, + &VectorGL_Test::constructCopy<3>, + + &VectorGL_Test::debugFlag, + &VectorGL_Test::debugFlags, + #ifndef MAGNUM_TARGET_GLES2 + &VectorGL_Test::debugFlagsSupersets + #endif + }); } template void VectorGL_Test::constructNoCreate() { @@ -87,6 +95,15 @@ void VectorGL_Test::debugFlags() { CORRADE_COMPARE(out.str(), "Shaders::VectorGL::Flag::TextureTransformation|Shaders::VectorGL::Flag(0xf0) Shaders::VectorGL::Flags{}\n"); } +#ifndef MAGNUM_TARGET_GLES2 +void VectorGL_Test::debugFlagsSupersets() { + /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + std::ostringstream out; + Debug{&out} << (VectorGL3D::Flag::MultiDraw|VectorGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::VectorGL::Flag::MultiDraw\n"); +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::VectorGL_Test) diff --git a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp index 3179ed70f..bff07e338 100644 --- a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp @@ -115,24 +115,27 @@ struct VertexColorGLTest: GL::OpenGLTester { Rendering tests done: [B] base - [O] draw offset + [O] UBOs + draw offset + [M] multidraw - Mesa Intel BO - ES2 x - ES3 BO + Mesa Intel BOM + ES2 xx + ES3 BOx Mesa AMD B Mesa llvmpipe B - SwiftShader ES2 Bx + SwiftShader ES2 Bxx ES3 B - ARM Mali (Huawei P10) ES2 Bx - ES3 BO - WebGL (on Mesa Intel) 1.0 Bx - 2.0 BO + ANGLE ES2 xx + ES3 BOM + ARM Mali (Huawei P10) ES2 Bxx + ES3 BOx + WebGL (on Mesa Intel) 1.0 Bxx + 2.0 BOM NVidia Intel Windows - AMD macOS - Intel macOS BO - iPhone 6 w/ iOS 12.4 ES3 B + AMD macOS x + Intel macOS BOx + iPhone 6 w/ iOS 12.4 ES3 B x */ using namespace Math::Literals; @@ -147,7 +150,8 @@ constexpr struct { {"", VertexColorGL2D::Flag::UniformBuffers, 1}, /* SwiftShader has 256 uniform vectors at most, per-draw is 4 in 3D case and 3 in 2D; one needs to be reserved for drawOffset */ - {"multiple draws", VertexColorGL2D::Flag::UniformBuffers, 63} + {"multiple draws", VertexColorGL2D::Flag::UniformBuffers, 63}, + {"multidraw with all the things", VertexColorGL2D::Flag::MultiDraw, 63} }; #endif @@ -156,18 +160,23 @@ constexpr struct { const char* name; const char* expected2D; const char* expected3D; + VertexColorGL2D::Flags flags; UnsignedInt drawCount; UnsignedInt uniformIncrement; Float maxThreshold, meanThreshold; } RenderMultiData[] { {"bind with offset", "multidraw2D.tga", "multidraw3D.tga", - 1, 16, + {}, 1, 16, /* Minor differences on ARM Mali */ 0.34f, 0.01f}, {"draw offset", "multidraw2D.tga", "multidraw3D.tga", - 3, 1, + {}, 3, 1, /* Minor differences on ARM Mali */ 0.34f, 0.01f}, + {"multidraw", "multidraw2D.tga", "multidraw3D.tga", + VertexColorGL2D::Flag::MultiDraw, 3, 1, + /* Minor differences on ARM Mali */ + 0.34f, 0.01f} }; #endif @@ -305,6 +314,19 @@ template void VertexColorGLTest::constructUniformBuffers CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= VertexColorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + VertexColorGL shader{data.flags, data.drawCount}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_COMPARE(shader.drawCount(), data.drawCount); @@ -758,6 +780,19 @@ void VertexColorGLTest::renderMulti2D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= VertexColorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + /* Circle is a fan, plane is a strip, make it indexed first */ Trade::MeshData circleData = MeshTools::generateIndices(Primitives::circle2DSolid(32)); Trade::MeshData squareData = MeshTools::generateIndices(Primitives::squareSolid()); @@ -806,7 +841,7 @@ void VertexColorGLTest::renderMulti2D() { ); GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, transformationProjectionData}; - VertexColorGL2D shader{VertexColorGL2D::Flag::UniformBuffers, data.drawCount}; + VertexColorGL2D shader{VertexColorGL2D::Flag::UniformBuffers|data.flags, data.drawCount}; /* Just one draw, rebinding UBOs each time */ if(data.drawCount == 1) { @@ -825,15 +860,20 @@ void VertexColorGLTest::renderMulti2D() { sizeof(TransformationProjectionUniform2D)); shader.draw(triangle); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform); - shader.setDrawOffset(0) - .draw(circle); - shader.setDrawOffset(1) - .draw(square); - shader.setDrawOffset(2) - .draw(triangle); + + if(data.flags >= VertexColorGL2D::Flag::MultiDraw) + shader.draw({circle, square, triangle}); + else { + shader.setDrawOffset(0) + .draw(circle); + shader.setDrawOffset(1) + .draw(square); + shader.setDrawOffset(2) + .draw(triangle); + } } MAGNUM_VERIFY_NO_GL_ERROR(); @@ -863,6 +903,19 @@ void VertexColorGLTest::renderMulti3D() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + if(data.flags >= VertexColorGL3D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + Trade::MeshData sphereData = Primitives::uvSphereSolid(16, 32); /* Plane is a strip, make it indexed first */ Trade::MeshData planeData = MeshTools::generateIndices(Primitives::planeSolid()); @@ -914,7 +967,7 @@ void VertexColorGLTest::renderMulti3D() { ); GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, transformationProjectionData}; - VertexColorGL3D shader{VertexColorGL3D::Flag::UniformBuffers, data.drawCount}; + VertexColorGL3D shader{VertexColorGL3D::Flag::UniformBuffers|data.flags, data.drawCount}; /* Just one draw, rebinding UBOs each time */ if(data.drawCount == 1) { @@ -933,15 +986,20 @@ void VertexColorGLTest::renderMulti3D() { sizeof(TransformationProjectionUniform3D)); shader.draw(cone); - /* Otherwise using the draw offset */ + /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform); - shader.setDrawOffset(0) - .draw(sphere); - shader.setDrawOffset(1) - .draw(plane); - shader.setDrawOffset(2) - .draw(cone); + + if(data.flags >= VertexColorGL3D::Flag::MultiDraw) + shader.draw({sphere, plane, cone}); + else { + shader.setDrawOffset(0) + .draw(sphere); + shader.setDrawOffset(1) + .draw(plane); + shader.setDrawOffset(2) + .draw(cone); + } } MAGNUM_VERIFY_NO_GL_ERROR(); diff --git a/src/Magnum/Shaders/Test/VertexColorGL_Test.cpp b/src/Magnum/Shaders/Test/VertexColorGL_Test.cpp index 1f86165ac..949670a1c 100644 --- a/src/Magnum/Shaders/Test/VertexColorGL_Test.cpp +++ b/src/Magnum/Shaders/Test/VertexColorGL_Test.cpp @@ -41,6 +41,9 @@ struct VertexColorGL_Test: TestSuite::Tester { void debugFlag(); void debugFlags(); + #ifndef MAGNUM_TARGET_GLES2 + void debugFlagsSupersets(); + #endif }; VertexColorGL_Test::VertexColorGL_Test() { @@ -52,7 +55,11 @@ VertexColorGL_Test::VertexColorGL_Test() { &VertexColorGL_Test::constructCopy<3>, &VertexColorGL_Test::debugFlag, - &VertexColorGL_Test::debugFlags}); + &VertexColorGL_Test::debugFlags, + #ifndef MAGNUM_TARGET_GLES2 + &VertexColorGL_Test::debugFlagsSupersets + #endif + }); } template void VertexColorGL_Test::constructNoCreate() { @@ -97,6 +104,15 @@ void VertexColorGL_Test::debugFlags() { #endif } +#ifndef MAGNUM_TARGET_GLES2 +void VertexColorGL_Test::debugFlagsSupersets() { + /* MultiDraw is a superset of UniformBuffers so only one should be printed */ + std::ostringstream out; + Debug{&out} << (VertexColorGL3D::Flag::MultiDraw|VertexColorGL3D::Flag::UniformBuffers); + CORRADE_COMPARE(out.str(), "Shaders::VertexColorGL::Flag::MultiDraw\n"); +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::VertexColorGL_Test) diff --git a/src/Magnum/Shaders/Vector.frag b/src/Magnum/Shaders/Vector.frag index 94f0bb419..8f1993944 100644 --- a/src/Magnum/Shaders/Vector.frag +++ b/src/Magnum/Shaders/Vector.frag @@ -53,6 +53,7 @@ uniform lowp vec4 color /* Uniform buffers */ #else +#ifndef MULTI_DRAW #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -61,6 +62,8 @@ uniform highp uint drawOffset = 0u #endif ; +#define drawId drawOffset +#endif struct DrawUniform { lowp vec4 color; @@ -88,6 +91,10 @@ uniform lowp sampler2D vectorTexture; in mediump vec2 interpolatedTextureCoordinates; +#ifdef MULTI_DRAW +flat in highp uint drawId; +#endif + /* Outputs */ #ifdef NEW_GLSL @@ -99,8 +106,8 @@ out lowp vec4 fragmentColor; void main() { #ifdef UNIFORM_BUFFERS - lowp const vec4 color = draws[drawOffset].color; - lowp const vec4 backgroundColor = draws[drawOffset].backgroundColor; + lowp const vec4 color = draws[drawId].color; + lowp const vec4 backgroundColor = draws[drawId].backgroundColor; #endif lowp float intensity = texture(vectorTexture, interpolatedTextureCoordinates).r; diff --git a/src/Magnum/Shaders/Vector.vert b/src/Magnum/Shaders/Vector.vert index e0fbab898..3f38ac103 100644 --- a/src/Magnum/Shaders/Vector.vert +++ b/src/Magnum/Shaders/Vector.vert @@ -23,6 +23,14 @@ DEALINGS IN THE SOFTWARE. */ +#ifdef MULTI_DRAW +#ifndef GL_ES +#extension GL_ARB_shader_draw_parameters: require +#else /* covers WebGL as well */ +#extension GL_ANGLE_multi_draw: require +#endif +#endif + #ifndef NEW_GLSL #define in attribute #define out varying @@ -132,8 +140,24 @@ in mediump vec2 textureCoordinates; out mediump vec2 interpolatedTextureCoordinates; +#ifdef MULTI_DRAW +flat out highp uint drawId; +#endif + void main() { #ifdef UNIFORM_BUFFERS + #ifdef MULTI_DRAW + drawId = drawOffset + uint( + #ifndef GL_ES + gl_DrawIDARB /* Using GL_ARB_shader_draw_parameters, not GLSL 4.6 */ + #else + gl_DrawID + #endif + ); + #else + #define drawId drawOffset + #endif + highp const #ifdef TWO_DIMENSIONS mat3 @@ -142,9 +166,9 @@ void main() { #else #error #endif - transformationProjectionMatrix = transformationProjectionMatrices[drawOffset]; + transformationProjectionMatrix = transformationProjectionMatrices[drawId]; #ifdef TEXTURE_TRANSFORMATION - mediump const mat3 textureMatrix = mat3(textureTransformations[drawOffset].rotationScaling.xy, 0.0, textureTransformations[drawOffset].rotationScaling.zw, 0.0, textureTransformations[drawOffset].textureTransformation_offset, 1.0); + mediump const mat3 textureMatrix = mat3(textureTransformations[drawId].rotationScaling.xy, 0.0, textureTransformations[drawId].rotationScaling.zw, 0.0, textureTransformations[drawId].textureTransformation_offset, 1.0); #endif #endif diff --git a/src/Magnum/Shaders/VectorGL.cpp b/src/Magnum/Shaders/VectorGL.cpp index e0c2531af..9efddeb6b 100644 --- a/src/Magnum/Shaders/VectorGL.cpp +++ b/src/Magnum/Shaders/VectorGL.cpp @@ -81,6 +81,17 @@ template VectorGL::VectorGL(const Flags flag if(flags >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_draw_parameters); + #elif !defined(MAGNUM_TARGET_WEBGL) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ANGLE::multi_draw); + #else + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::WEBGL::multi_draw); + #endif + } + #endif #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ @@ -108,6 +119,7 @@ template VectorGL::VectorGL(const Flags flag "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", drawCount)); + vert.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif vert.addSource(rs.get("generic.glsl")) @@ -118,6 +130,7 @@ template VectorGL::VectorGL(const Flags flag "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", drawCount)); + frag.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif frag.addSource(rs.get("generic.glsl")) @@ -308,6 +321,7 @@ Debug& operator<<(Debug& debug, const VectorGLFlag value) { _c(TextureTransformation) #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + _c(MultiDraw) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -320,6 +334,7 @@ Debug& operator<<(Debug& debug, const VectorGLFlags value) { return Containers::enumSetDebugOutput(debug, value, "Shaders::VectorGL::Flags{}", { VectorGLFlag::TextureTransformation, #ifndef MAGNUM_TARGET_GLES2 + VectorGLFlag::MultiDraw, /* Superset of UniformBuffers */ VectorGLFlag::UniformBuffers #endif }); diff --git a/src/Magnum/Shaders/VectorGL.h b/src/Magnum/Shaders/VectorGL.h index c899e5130..898ac1ba3 100644 --- a/src/Magnum/Shaders/VectorGL.h +++ b/src/Magnum/Shaders/VectorGL.h @@ -41,7 +41,8 @@ namespace Implementation { enum class VectorGLFlag: UnsignedByte { TextureTransformation = 1 << 0, #ifndef MAGNUM_TARGET_GLES2 - UniformBuffers = 1 << 1 + UniformBuffers = 1 << 1, + MultiDraw = UniformBuffers|(1 << 2) #endif }; typedef Containers::EnumSet VectorGLFlags; @@ -135,7 +136,31 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * 1.0. * @m_since_latest */ - UniformBuffers = 1 << 1 + UniformBuffers = 1 << 1, + + /** + * Enable multidraw functionality. Implies @ref Flag::UniformBuffers + * and adds the value from @ref setDrawOffset() with the + * @glsl gl_DrawID @ce builtin, which makes draws submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up per-draw parameters directly, without having to rebind + * the uniform buffers or specify @ref setDrawOffset() before each + * draw. In a non-multidraw scenario, @glsl gl_DrawID @ce is + * @cpp 0 @ce, which means a shader with this flag enabled can be + * used for regular draws as well. + * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} + * and @gl_extension{ARB,shader_draw_parameters} + * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). + * While the extension alone needs only OpenGL ES 2.0, the + * shader implementation relies on uniform buffers, which + * require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. + * While the extension alone needs only WebGL 1.0, the shader + * implementation relies on uniform buffers, which require + * WebGL 2.0. + * @m_since_latest + */ + MultiDraw = UniformBuffers|(1 << 2) #endif }; @@ -327,6 +352,11 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * should be used for current draw. Expects that * @ref Flag::UniformBuffers is set and @p offset is less than * @ref drawCount(). Initial value is @cpp 0 @ce. + * + * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this + * value, which makes each draw submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up its own per-draw parameters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. diff --git a/src/Magnum/Shaders/VertexColor.vert b/src/Magnum/Shaders/VertexColor.vert index c7597a413..53f389cae 100644 --- a/src/Magnum/Shaders/VertexColor.vert +++ b/src/Magnum/Shaders/VertexColor.vert @@ -23,6 +23,14 @@ DEALINGS IN THE SOFTWARE. */ +#ifdef MULTI_DRAW +#ifndef GL_ES +#extension GL_ARB_shader_draw_parameters: require +#else /* covers WebGL as well */ +#extension GL_ANGLE_multi_draw: require +#endif +#endif + #ifndef NEW_GLSL #define in attribute #define out varying @@ -107,6 +115,18 @@ out lowp vec4 interpolatedColor; void main() { #ifdef UNIFORM_BUFFERS + #ifdef MULTI_DRAW + highp uint drawId = drawOffset + uint( + #ifndef GL_ES + gl_DrawIDARB /* Using GL_ARB_shader_draw_parameters, not GLSL 4.6 */ + #else + gl_DrawID + #endif + ); + #else + #define drawId drawOffset + #endif + highp const #ifdef TWO_DIMENSIONS mat3 @@ -115,7 +135,7 @@ void main() { #else #error #endif - transformationProjectionMatrix = transformationProjectionMatrices[drawOffset]; + transformationProjectionMatrix = transformationProjectionMatrices[drawId]; #endif #ifdef TWO_DIMENSIONS diff --git a/src/Magnum/Shaders/VertexColorGL.cpp b/src/Magnum/Shaders/VertexColorGL.cpp index 4c1b4eb3f..7da23cbc4 100644 --- a/src/Magnum/Shaders/VertexColorGL.cpp +++ b/src/Magnum/Shaders/VertexColorGL.cpp @@ -76,6 +76,17 @@ template VertexColorGL::VertexColorGL(const if(flags >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_draw_parameters); + #elif !defined(MAGNUM_TARGET_WEBGL) + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ANGLE::multi_draw); + #else + MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::WEBGL::multi_draw); + #endif + } + #endif #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ @@ -102,6 +113,7 @@ template VertexColorGL::VertexColorGL(const "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", drawCount)); + vert.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif vert.addSource(rs.get("generic.glsl")) @@ -214,6 +226,7 @@ Debug& operator<<(Debug& debug, const VertexColorGLFlag value) { #define _c(v) case VertexColorGLFlag::v: return debug << "::" #v; #ifndef MAGNUM_TARGET_GLES2 _c(UniformBuffers) + _c(MultiDraw) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -225,6 +238,7 @@ Debug& operator<<(Debug& debug, const VertexColorGLFlag value) { Debug& operator<<(Debug& debug, const VertexColorGLFlags value) { return Containers::enumSetDebugOutput(debug, value, "Shaders::VertexColorGL::Flags{}", { #ifndef MAGNUM_TARGET_GLES2 + VertexColorGLFlag::MultiDraw, /* Superset of UniformBuffers */ VertexColorGLFlag::UniformBuffers #endif }); diff --git a/src/Magnum/Shaders/VertexColorGL.h b/src/Magnum/Shaders/VertexColorGL.h index d84acec62..7df6556e1 100644 --- a/src/Magnum/Shaders/VertexColorGL.h +++ b/src/Magnum/Shaders/VertexColorGL.h @@ -40,7 +40,8 @@ namespace Magnum { namespace Shaders { namespace Implementation { enum class VertexColorGLFlag: UnsignedByte { #ifndef MAGNUM_TARGET_GLES2 - UniformBuffers = 1 << 0 + UniformBuffers = 1 << 0, + MultiDraw = UniformBuffers|(1 << 1) #endif }; typedef Containers::EnumSet VertexColorGLFlags; @@ -136,7 +137,31 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ * 1.0. * @m_since_latest */ - UniformBuffers = 1 << 0 + UniformBuffers = 1 << 0, + + /** + * Enable multidraw functionality. Implies @ref Flag::UniformBuffers + * and adds the value from @ref setDrawOffset() with the + * @glsl gl_DrawID @ce builtin, which makes draws submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up per-draw parameters directly, without having to rebind + * the uniform buffers or specify @ref setDrawOffset() before each + * draw. In a non-multidraw scenario, @glsl gl_DrawID @ce is + * @cpp 0 @ce, which means a shader with this flag enabled can be + * used for regular draws as well. + * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} + * and @gl_extension{ARB,shader_draw_parameters} + * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). + * While the extension alone needs only OpenGL ES 2.0, the + * shader implementation relies on uniform buffers, which + * require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. + * While the extension alone needs only WebGL 1.0, the shader + * implementation relies on uniform buffers, which require + * WebGL 2.0. + * @m_since_latest + */ + MultiDraw = UniformBuffers|(1 << 1) #endif }; @@ -279,6 +304,11 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ * @ref bindTransformationProjectionBuffer() should be used for current * draw. Expects that @ref Flag::UniformBuffers is set and @p offset is * less than @ref drawCount(). Initial value is @cpp 0 @ce. + * + * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this + * value, which makes each draw submitted via + * @ref GL::AbstractShaderProgram::draw(Containers::ArrayView>) + * pick up its own per-draw parameters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. From 5997d446a9c92d381cd45f45bb46620d4d6bfa78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 26 May 2021 23:31:32 +0200 Subject: [PATCH 37/93] Shaders: add multidraw benchmark variants. --- src/Magnum/Shaders/DistanceFieldVectorGL.h | 17 +-- src/Magnum/Shaders/FlatGL.h | 17 +-- src/Magnum/Shaders/MeshVisualizerGL.h | 34 ++--- src/Magnum/Shaders/PhongGL.h | 17 +-- .../Shaders/Test/ShadersGLBenchmark.cpp | 120 ++++++++++++++++++ src/Magnum/Shaders/VectorGL.h | 17 +-- src/Magnum/Shaders/VertexColorGL.h | 17 +-- 7 files changed, 183 insertions(+), 56 deletions(-) diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.h b/src/Magnum/Shaders/DistanceFieldVectorGL.h index 4284cd7c1..ffd3a40fe 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.h +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.h @@ -155,14 +155,15 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * used for regular draws as well. * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} * and @gl_extension{ARB,shader_draw_parameters} - * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). - * While the extension alone needs only OpenGL ES 2.0, the - * shader implementation relies on uniform buffers, which - * require OpenGL ES 3.0. - * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. - * While the extension alone needs only WebGL 1.0, the shader - * implementation relies on uniform buffers, which require - * WebGL 2.0. + * @requires_es_extension OpenGL ES 3.0 and extension + * @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + * (unlisted). While the extension alone needs only OpenGL ES + * 2.0, the shader implementation relies on uniform buffers, + * which require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 and extension + * @webgl_extension{ANGLE,multi_draw}. While the extension + * alone needs only WebGL 1.0, the shader implementation + * relies on uniform buffers, which require WebGL 2.0. * @m_since_latest */ MultiDraw = UniformBuffers|(1 << 2) diff --git a/src/Magnum/Shaders/FlatGL.h b/src/Magnum/Shaders/FlatGL.h index 1daa1c6c4..421b240b6 100644 --- a/src/Magnum/Shaders/FlatGL.h +++ b/src/Magnum/Shaders/FlatGL.h @@ -403,14 +403,15 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * used for regular draws as well. * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} * and @gl_extension{ARB,shader_draw_parameters} - * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). - * While the extension alone needs only OpenGL ES 2.0, the - * shader implementation relies on uniform buffers, which - * require OpenGL ES 3.0. - * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. - * While the extension alone needs only WebGL 1.0, the shader - * implementation relies on uniform buffers, which require - * WebGL 2.0. + * @requires_es_extension OpenGL ES 3.0 and extension + * @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + * (unlisted). While the extension alone needs only OpenGL ES + * 2.0, the shader implementation relies on uniform buffers, + * which require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 and extension + * @webgl_extension{ANGLE,multi_draw}. While the extension + * alone needs only WebGL 1.0, the shader implementation + * relies on uniform buffers, which require WebGL 2.0. * @m_since_latest */ MultiDraw = UniformBuffers|(1 << 9) diff --git a/src/Magnum/Shaders/MeshVisualizerGL.h b/src/Magnum/Shaders/MeshVisualizerGL.h index 6e0df5a3c..d96773057 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.h +++ b/src/Magnum/Shaders/MeshVisualizerGL.h @@ -251,14 +251,15 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * used for regular draws as well. * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} * and @gl_extension{ARB,shader_draw_parameters} - * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). - * While the extension alone needs only OpenGL ES 2.0, the - * shader implementation relies on uniform buffers, which - * require OpenGL ES 3.0. - * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. - * While the extension alone needs only WebGL 1.0, the shader - * implementation relies on uniform buffers, which require - * WebGL 2.0. + * @requires_es_extension OpenGL ES 3.0 and extension + * @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + * (unlisted). While the extension alone needs only OpenGL ES + * 2.0, the shader implementation relies on uniform buffers, + * which require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 and extension + * @webgl_extension{ANGLE,multi_draw}. While the extension + * alone needs only WebGL 1.0, the shader implementation + * relies on uniform buffers, which require WebGL 2.0. * @m_since_latest */ MultiDraw = UniformBuffers|(1 << 11) @@ -1041,14 +1042,15 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * used for regular draws as well. * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} * and @gl_extension{ARB,shader_draw_parameters} - * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). - * While the extension alone needs only OpenGL ES 2.0, the - * shader implementation relies on uniform buffers, which - * require OpenGL ES 3.0. - * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. - * While the extension alone needs only WebGL 1.0, the shader - * implementation relies on uniform buffers, which require - * WebGL 2.0. + * @requires_es_extension OpenGL ES 3.0 and extension + * @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + * (unlisted). While the extension alone needs only OpenGL ES + * 2.0, the shader implementation relies on uniform buffers, + * which require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 and extension + * @webgl_extension{ANGLE,multi_draw}. While the extension + * alone needs only WebGL 1.0, the shader implementation + * relies on uniform buffers, which require WebGL 2.0. * @m_since_latest */ MultiDraw = UniformBuffers|(1 << 11) diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index 7e949613c..1e0f743e3 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -591,14 +591,15 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * used for regular draws as well. * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} * and @gl_extension{ARB,shader_draw_parameters} - * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). - * While the extension alone needs only OpenGL ES 2.0, the - * shader implementation relies on uniform buffers, which - * require OpenGL ES 3.0. - * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. - * While the extension alone needs only WebGL 1.0, the shader - * implementation relies on uniform buffers, which require - * WebGL 2.0. + * @requires_es_extension OpenGL ES 3.0 and extension + * @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + * (unlisted). While the extension alone needs only OpenGL ES + * 2.0, the shader implementation relies on uniform buffers, + * which require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 and extension + * @webgl_extension{ANGLE,multi_draw}. While the extension + * alone needs only WebGL 1.0, the shader implementation + * relies on uniform buffers, which require WebGL 2.0. * @m_since_latest */ MultiDraw = UniformBuffers|(1 << 13) diff --git a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp index 6a2d42ff4..814059a16 100644 --- a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp +++ b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp @@ -150,6 +150,7 @@ const struct { {"UBO single", FlatGL2D::Flag::UniformBuffers, 1}, {"UBO single, texture transformation", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1}, {"UBO multi", FlatGL2D::Flag::UniformBuffers, 128}, + {"multidraw", FlatGL2D::Flag::MultiDraw, 128}, #endif }; @@ -183,6 +184,7 @@ const struct { {"UBO single five lights", PhongGL::Flag::UniformBuffers, 5, 1, 1}, {"UBO single, ADS textures + transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureTransformation, 1, 1, 1}, {"UBO multi, one light", PhongGL::Flag::UniformBuffers, 1, 32, 128}, + {"multidraw, one light", PhongGL::Flag::MultiDraw, 1, 32, 128}, #endif }; @@ -195,6 +197,7 @@ const struct { #ifndef MAGNUM_TARGET_GLES2 {"UBO single", VertexColorGL2D::Flag::UniformBuffers, 1}, {"UBO multi", VertexColorGL2D::Flag::UniformBuffers, 128}, + {"multidraw", VertexColorGL2D::Flag::MultiDraw, 128} #endif }; @@ -210,6 +213,7 @@ const struct { {"UBO single, texture transformation", VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, 1}, {"UBO multi", VectorGL2D::Flag::UniformBuffers, 128}, {"UBO multi, texture transformation", VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, 128}, + {"multidraw", VectorGL2D::Flag::MultiDraw, 128}, #endif }; @@ -225,6 +229,7 @@ const struct { {"UBO single, texture transformation", DistanceFieldVectorGL2D::Flag::UniformBuffers|DistanceFieldVectorGL2D::Flag::TextureTransformation, 1, 1}, {"UBO multi", DistanceFieldVectorGL2D::Flag::UniformBuffers, 32, 128}, {"UBO multi, texture transformation", DistanceFieldVectorGL2D::Flag::UniformBuffers|DistanceFieldVectorGL2D::Flag::TextureTransformation, 32, 128}, + {"multidraw", DistanceFieldVectorGL2D::Flag::MultiDraw, 32, 128}, #endif }; @@ -256,6 +261,11 @@ const struct { #endif {"UBO multi, wireframe w/o a GS", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 32, 128}, {"UBO multi, vertex ID", MeshVisualizerGL2D::Flag::UniformBuffers|MeshVisualizerGL2D::Flag::VertexId, 32, 128}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, wireframe", MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::Wireframe, 32, 128}, + #endif + {"multidraw, wireframe w/o a GS", MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::Wireframe|MeshVisualizerGL2D::Flag::NoGeometryShader, 32, 128}, + {"multidraw, vertex ID", MeshVisualizerGL2D::Flag::MultiDraw|MeshVisualizerGL2D::Flag::VertexId, 32, 128}, #endif }; @@ -287,6 +297,11 @@ const struct { #endif {"UBO multi, wireframe w/o a GS", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 32, 128}, {"UBO multi, vertex ID", MeshVisualizerGL3D::Flag::UniformBuffers|MeshVisualizerGL3D::Flag::VertexId, 32, 128}, + #ifndef MAGNUM_TARGET_WEBGL + {"multidraw, wireframe", MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe, 32, 128}, + #endif + {"multidraw, wireframe w/o a GS", MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::Wireframe|MeshVisualizerGL3D::Flag::NoGeometryShader, 32, 128}, + {"multidraw, vertex ID", MeshVisualizerGL3D::Flag::MultiDraw|MeshVisualizerGL3D::Flag::VertexId, 32, 128}, #endif }; @@ -509,6 +524,21 @@ template void ShadersGLBenchmark::flat() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags >= FlatGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + #endif + FlatGL shader{data.flags #ifndef MAGNUM_TARGET_GLES2 , data.drawCount @@ -594,6 +624,21 @@ void ShadersGLBenchmark::phong() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags >= PhongGL::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + #endif + PhongGL shader{data.flags, data.lightCount #ifndef MAGNUM_TARGET_GLES2 , data.materialCount, data.drawCount @@ -702,6 +747,21 @@ template void ShadersGLBenchmark::vertexColor() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags >= VertexColorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + #endif + VertexColorGL shader{data.flags #ifndef MAGNUM_TARGET_GLES2 , data.drawCount @@ -746,6 +806,21 @@ template void ShadersGLBenchmark::vector() { CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags >= VectorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + #endif + VectorGL shader{data.flags #ifndef MAGNUM_TARGET_GLES2 , data.drawCount @@ -800,6 +875,21 @@ template void ShadersGLBenchmark::distanceFieldVector() CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags >= DistanceFieldVectorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + #endif + DistanceFieldVectorGL shader{data.flags #ifndef MAGNUM_TARGET_GLES2 , data.materialCount, data.drawCount @@ -897,6 +987,21 @@ void ShadersGLBenchmark::meshVisualizer2D() { } #endif + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags >= MeshVisualizerGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + #endif + MeshVisualizerGL2D shader{data.flags}; shader.setViewportSize(Vector2{RenderSize}); #ifndef MAGNUM_TARGET_GLES2 @@ -1016,6 +1121,21 @@ void ShadersGLBenchmark::meshVisualizer3D() { } #endif + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags >= MeshVisualizerGL3D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + #endif + MeshVisualizerGL3D shader{data.flags}; shader.setViewportSize(Vector2{RenderSize}); diff --git a/src/Magnum/Shaders/VectorGL.h b/src/Magnum/Shaders/VectorGL.h index 898ac1ba3..d5fd88d10 100644 --- a/src/Magnum/Shaders/VectorGL.h +++ b/src/Magnum/Shaders/VectorGL.h @@ -150,14 +150,15 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * used for regular draws as well. * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} * and @gl_extension{ARB,shader_draw_parameters} - * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). - * While the extension alone needs only OpenGL ES 2.0, the - * shader implementation relies on uniform buffers, which - * require OpenGL ES 3.0. - * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. - * While the extension alone needs only WebGL 1.0, the shader - * implementation relies on uniform buffers, which require - * WebGL 2.0. + * @requires_es_extension OpenGL ES 3.0 and extension + * @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + * (unlisted). While the extension alone needs only OpenGL ES + * 2.0, the shader implementation relies on uniform buffers, + * which require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 and extension + * @webgl_extension{ANGLE,multi_draw}. While the extension + * alone needs only WebGL 1.0, the shader implementation + * relies on uniform buffers, which require WebGL 2.0. * @m_since_latest */ MultiDraw = UniformBuffers|(1 << 2) diff --git a/src/Magnum/Shaders/VertexColorGL.h b/src/Magnum/Shaders/VertexColorGL.h index 7df6556e1..c13e29f76 100644 --- a/src/Magnum/Shaders/VertexColorGL.h +++ b/src/Magnum/Shaders/VertexColorGL.h @@ -151,14 +151,15 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ * used for regular draws as well. * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} * and @gl_extension{ARB,shader_draw_parameters} - * @requires_es_extension OpenGL ES 3.0 and extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted). - * While the extension alone needs only OpenGL ES 2.0, the - * shader implementation relies on uniform buffers, which - * require OpenGL ES 3.0. - * @requires_webgl_extension WebGL 2.0 Extension @webgl_extension{ANGLE,multi_draw}. - * While the extension alone needs only WebGL 1.0, the shader - * implementation relies on uniform buffers, which require - * WebGL 2.0. + * @requires_es_extension OpenGL ES 3.0 and extension + * @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + * (unlisted). While the extension alone needs only OpenGL ES + * 2.0, the shader implementation relies on uniform buffers, + * which require OpenGL ES 3.0. + * @requires_webgl_extension WebGL 2.0 and extension + * @webgl_extension{ANGLE,multi_draw}. While the extension + * alone needs only WebGL 1.0, the shader implementation + * relies on uniform buffers, which require WebGL 2.0. * @m_since_latest */ MultiDraw = UniformBuffers|(1 << 1) From a2f8a920dac5b7b60546d0edfe4ae765dff7ab6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 27 May 2021 16:34:47 +0200 Subject: [PATCH 38/93] Shaders: supply Flat material information in a separate UBO as well. While it's one additional indirection (that has an extra cost on Intel GPUs apparently, like with Phong and MeshVisualizer and DistanceFieldVector already), with the assumption that draws usually share the material info it allows to cram more draws into the 16/64k UBO limit as the per-draw data are now one vec4 smaller. For the indirection overhead I can imagine adding a new flag which makes material mapping implicit (materialId == drawId). That seems to put the benchmark numbers back to the original speed. Same could be done for other shaders. --- src/Magnum/Shaders/Flat.frag | 30 ++- src/Magnum/Shaders/Flat.h | 182 ++++++++++---- src/Magnum/Shaders/FlatGL.cpp | 32 ++- src/Magnum/Shaders/FlatGL.h | 86 +++++-- src/Magnum/Shaders/Test/FlatGLTest.cpp | 233 ++++++++++++++---- src/Magnum/Shaders/Test/FlatTest.cpp | 97 ++++++-- .../Shaders/Test/ShadersGLBenchmark.cpp | 43 ++-- 7 files changed, 525 insertions(+), 178 deletions(-) diff --git a/src/Magnum/Shaders/Flat.frag b/src/Magnum/Shaders/Flat.frag index b9fa74103..c63b22d41 100644 --- a/src/Magnum/Shaders/Flat.frag +++ b/src/Magnum/Shaders/Flat.frag @@ -27,10 +27,6 @@ #extension GL_EXT_gpu_shader4: require #endif -#if defined(UNIFORM_BUFFERS) && defined(ALPHA_MASK) && !defined(GL_ES) -#extension GL_ARB_shader_bit_encoding: require -#endif - #ifndef NEW_GLSL #define fragmentColor gl_FragColor #define texture texture2D @@ -88,10 +84,9 @@ uniform highp uint drawOffset #endif struct DrawUniform { - lowp vec4 color; - highp uvec4 objectIdReservedAlphaMaskReserved; - #define draw_objectId objectIdReservedAlphaMaskReserved.x - #define draw_alphaMask objectIdReservedAlphaMaskReserved.z + highp uvec4 materialIdReservedObjectIdReservedReserved; + #define draw_materialIdReserved materialIdReservedObjectIdReservedReserved.x + #define draw_objectId materialIdReservedObjectIdReservedReserved.y }; layout(std140 @@ -101,6 +96,20 @@ layout(std140 ) uniform Draw { DrawUniform draws[DRAW_COUNT]; }; + +struct MaterialUniform { + lowp vec4 color; + highp vec4 alphaMaskReservedReservedReserved; + #define material_alphaMask alphaMaskReservedReservedReserved.x +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 4 + #endif +) uniform Material { + MaterialUniform materials[MATERIAL_COUNT]; +}; #endif /* Textures */ @@ -148,12 +157,13 @@ flat in highp uint drawId; void main() { #ifdef UNIFORM_BUFFERS - lowp const vec4 color = draws[drawId].color; #ifdef OBJECT_ID highp const uint objectId = draws[drawId].draw_objectId; #endif + mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; + lowp const vec4 color = materials[materialId].color; #ifdef ALPHA_MASK - lowp const float alphaMask = uintBitsToFloat(draws[drawId].draw_alphaMask); + lowp const float alphaMask = materials[materialId].material_alphaMask; #endif #endif diff --git a/src/Magnum/Shaders/Flat.h b/src/Magnum/Shaders/Flat.h index e5bef400b..4a894c173 100644 --- a/src/Magnum/Shaders/Flat.h +++ b/src/Magnum/Shaders/Flat.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Struct @ref Magnum::Shaders::FlatDrawUniform + * @brief Struct @ref Magnum::Shaders::FlatDrawUniform, @ref Magnum::Shaders::FlatMaterialUniform */ #include "Magnum/Magnum.h" @@ -47,24 +47,29 @@ namespace Magnum { namespace Shaders { Together with the generic @ref TransformationProjectionUniform2D / @ref TransformationProjectionUniform3D contains parameters that are specific to each draw call. Texture transformation, if needed, is supplied separately in a -@ref TextureTransformationUniform. +@ref TextureTransformationUniform; material-related properties are expected to +be shared among multiple draw calls and thus are provided in a separate +@ref FlatMaterialUniform structure, referenced by @ref materialId. @see @ref FlatGL::bindDrawBuffer() */ struct FlatDrawUniform { /** @brief Construct with default parameters */ - constexpr explicit FlatDrawUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f}, objectId{0}, - #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) - /* Otherwise it refuses to constexpr, on 3.8 at least */ - _pad0{}, _pad1{}, + constexpr explicit FlatDrawUniform(DefaultInitT = DefaultInit) noexcept: + #if ((defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8)) && defined(CORRADE_TARGET_BIG_ENDIAN) + _pad0{}, /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + materialId{0}, + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) && !defined(CORRADE_TARGET_BIG_ENDIAN) + _pad0{}, #endif - alphaMask{0.5f} + objectId{0} #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) - , _pad2{} + , _pad1{}, _pad2{} #endif {} /** @brief Construct without initializing the contents */ - explicit FlatDrawUniform(NoInitT) noexcept: color{NoInit} {} + explicit FlatDrawUniform(NoInitT) noexcept {} /** @{ * @name Convenience setters @@ -76,29 +81,20 @@ struct FlatDrawUniform { */ /** - * @brief Set the @ref objectId field + * @brief Set the @ref materialId field * @return Reference to self (for method chaining) */ - FlatDrawUniform& setObjectId(UnsignedInt id) { - objectId = id; + FlatDrawUniform& setMaterialId(UnsignedInt id) { + materialId = id; return *this; } /** - * @brief Set the @ref alphaMask field - * @return Reference to self (for method chaining) - */ - FlatDrawUniform& setAlphaMask(Float alphaMask) { - this->alphaMask = alphaMask; - return *this; - } - - /** - * @brief Set the @ref color field + * @brief Set the @ref objectId field * @return Reference to self (for method chaining) */ - FlatDrawUniform& setColor(const Color4& color) { - this->color = color; + FlatDrawUniform& setObjectId(UnsignedInt id) { + objectId = id; return *this; } @@ -106,17 +102,37 @@ struct FlatDrawUniform { * @} */ - /** - * @brief Color - * - * Default value is @cpp 0xffffffff_rgbaf @ce. + /** @var materialId + * @brief Material ID * - * If @ref FlatGL::Flag::VertexColor is enabled, the color is multiplied - * with a color coming from the @ref FlatGL::Color3 / @ref FlatGL::Color4 - * attribute. - * @see @ref FlatGL::setColor() + * References a particular material from a @ref FlatMaterialUniform array. + * Useful when an UBO with more than one material is supplied or in a + * multi-draw scenario. Should be less than the material count passed to + * the @ref FlatGL::FlatGL(Flags, UnsignedInt, UnsignedInt) constructor. + * Default value is @cpp 0 @ce, meaning the first material gets used. */ - Color4 color; + + /* This field is an UnsignedInt in the shader and materialId is extracted + as (value & 0xffff), so the order has to be different on BE */ + #ifndef CORRADE_TARGET_BIG_ENDIAN + UnsignedShort materialId; + /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; /* reserved for skinOffset */ + #endif + #else + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; /* reserved for skinOffset */ + UnsignedShort materialId; + #endif /** * @brief Object ID @@ -131,35 +147,83 @@ struct FlatDrawUniform { */ UnsignedInt objectId; - /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK + /* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - /* This field is an UnsignedInt in the shader and skinOffset is extracted - as (value >> 16), so the order has to be different on BE */ - #ifndef CORRADE_TARGET_BIG_ENDIAN - UnsignedShort - #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) - _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ - #endif - :16; - UnsignedShort + Int #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ #endif - :16; /* reserved for skinOffset */ - #else - UnsignedShort + :32; + Int #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) - _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + _pad2 /* Otherwise it refuses to constexpr, on 3.8 at least */ #endif - :16; /* reserved for skinOffset */ - UnsignedShort + :32; + #endif +}; + +/** +@brief Material uniform for flat shaders +@m_since_latest + +Describes material properties referenced from +@ref FlatDrawUniform::materialId. +@see @ref FlatGL::bindMaterialBuffer() +*/ +struct FlatMaterialUniform { + /** @brief Construct with default parameters */ + constexpr explicit FlatMaterialUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f}, alphaMask{0.5f} #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) - _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ + , _pad0{}, _pad1{}, _pad2{} #endif - :16; - #endif - #endif + {} + + /** @brief Construct without initializing the contents */ + explicit FlatMaterialUniform(NoInitT) noexcept: color{NoInit} {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref alphaMask field + * @return Reference to self (for method chaining) + */ + FlatMaterialUniform& setAlphaMask(Float alphaMask) { + this->alphaMask = alphaMask; + return *this; + } + + /** + * @brief Set the @ref color field + * @return Reference to self (for method chaining) + */ + FlatMaterialUniform& setColor(const Color4& color) { + this->color = color; + return *this; + } + + /** + * @} + */ + + /** + * @brief Color + * + * Default value is @cpp 0xffffffff_rgbaf @ce. + * + * If @ref FlatGL::Flag::VertexColor is enabled, the color is multiplied + * with a color coming from the @ref FlatGL::Color3 / @ref FlatGL::Color4 + * attribute. + * @see @ref FlatGL::setColor() + */ + Color4 color; /** * @brief Alpha mask value @@ -172,9 +236,19 @@ struct FlatDrawUniform { */ Float alphaMask; - /* warning: Member __pad2__ is not documented. FFS DOXYGEN WHY DO YOU THINK + /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; Int #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) _pad2 /* Otherwise it refuses to constexpr, on 3.8 at least */ diff --git a/src/Magnum/Shaders/FlatGL.cpp b/src/Magnum/Shaders/FlatGL.cpp index c91221fb6..4df4cdffb 100644 --- a/src/Magnum/Shaders/FlatGL.cpp +++ b/src/Magnum/Shaders/FlatGL.cpp @@ -57,25 +57,28 @@ namespace { bound to the same buffer for the whole time */ TransformationProjectionBufferBinding = 1, DrawBufferBinding = 2, - TextureTransformationBufferBinding = 3 + TextureTransformationBufferBinding = 3, + MaterialBufferBinding = 4 }; #endif } template FlatGL::FlatGL(const Flags flags #ifndef MAGNUM_TARGET_GLES2 - , const UnsignedInt drawCount + , const UnsignedInt materialCount, const UnsignedInt drawCount #endif ): _flags{flags} #ifndef MAGNUM_TARGET_GLES2 - , _drawCount{drawCount} + , _materialCount{materialCount}, _drawCount{drawCount} #endif { CORRADE_ASSERT(!(flags & Flag::TextureTransformation) || (flags & Flag::Textured), "Shaders::FlatGL: texture transformation enabled but the shader is not textured", ); #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || materialCount, + "Shaders::FlatGL: material count can't be zero", ); CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || drawCount, "Shaders::FlatGL: draw count can't be zero", ); #endif @@ -146,8 +149,10 @@ template FlatGL::FlatGL(const Flags flags if(flags >= Flag::UniformBuffers) { frag.addSource(Utility::formatString( "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n", - drawCount)); + "#define DRAW_COUNT {}\n" + "#define MATERIAL_COUNT {}\n", + drawCount, + materialCount)); frag.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif @@ -219,6 +224,7 @@ template FlatGL::FlatGL(const Flags flags setUniformBlockBinding(uniformBlockIndex("Draw"), DrawBufferBinding); if(flags & Flag::TextureTransformation) setUniformBlockBinding(uniformBlockIndex("TextureTransformation"), TextureTransformationBufferBinding); + setUniformBlockBinding(uniformBlockIndex("Material"), MaterialBufferBinding); } #endif } @@ -242,7 +248,7 @@ template FlatGL::FlatGL(const Flags flags } #ifndef MAGNUM_TARGET_GLES2 -template FlatGL::FlatGL(const Flags flags): FlatGL{flags, 1} {} +template FlatGL::FlatGL(const Flags flags): FlatGL{flags, 1, 1} {} #endif template FlatGL& FlatGL::setTransformationProjectionMatrix(const MatrixTypeFor& matrix) { @@ -351,6 +357,20 @@ template FlatGL& FlatGL::bindTex buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); return *this; } + +template FlatGL& FlatGL::bindMaterialBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::FlatGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding); + return *this; +} + +template FlatGL& FlatGL::bindMaterialBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::FlatGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); + return *this; +} #endif template FlatGL& FlatGL::bindTexture(GL::Texture2D& texture) { diff --git a/src/Magnum/Shaders/FlatGL.h b/src/Magnum/Shaders/FlatGL.h index 421b240b6..6e86fe668 100644 --- a/src/Magnum/Shaders/FlatGL.h +++ b/src/Magnum/Shaders/FlatGL.h @@ -380,8 +380,8 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: /** * Use uniform buffers. Expects that uniform data are supplied via * @ref bindTransformationProjectionBuffer(), - * @ref bindDrawBuffer() and @ref bindTextureTransformationBuffer() - * instead of direct uniform setters. + * @ref bindDrawBuffer(), @ref bindTextureTransformationBuffer() + * and @ref bindMaterialBuffer() instead of direct uniform setters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES * 2.0. @@ -437,32 +437,44 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * * While this function is meant mainly for the classic uniform * scenario (without @ref Flag::UniformBuffers set), it's equivalent to - * @ref FlatGL(Flags, UnsignedInt) with @p drawCount set to @cpp 1 @ce. + * @ref FlatGL(Flags, UnsignedInt, UnsignedInt) with @p materialCount + * and @p drawCount set to @cpp 1 @ce. */ explicit FlatGL(Flags flags = {}); #ifndef MAGNUM_TARGET_GLES2 /** * @brief Construct for a multi-draw scenario - * @param flags Flags - * @param drawCount Size of a @ref TransformationProjectionUniform2D / - * @ref TransformationProjectionUniform3D / @ref FlatDrawUniform / - * @ref TextureTransformationUniform buffer bound with + * @param flags Flags + * @param materialCount Size of a @ref FlatMaterialUniform buffer + * bound with @ref bindMaterialBuffer() + * @param drawCount Size of a @ref TransformationProjectionUniform2D + * / @ref TransformationProjectionUniform3D / @ref FlatDrawUniform + * / @ref TextureTransformationUniform buffer bound with * @ref bindTransformationProjectionBuffer(), @ref bindDrawBuffer() * and @ref bindTextureTransformationBuffer() * - * If @p flags contains @ref Flag::UniformBuffers, @p drawCount - * describes the uniform buffer sizes as these are required to have a - * statically defined size. The draw offset is then set via - * @ref setDrawOffset(). + * If @p flags contains @ref Flag::UniformBuffers, @p materialCount and + * @p drawCount describe the uniform buffer sizes as these are required + * to have a statically defined size. The draw offset is then set via + * @ref setDrawOffset() and the per-draw materials specified via + * @ref FlatDrawUniform::materialId. * - * If @p flags don't contain @ref Flag::UniformBuffers, @p drawCount is - * ignored and the constructor behaves the same as @ref FlatGL(Flags). + * If @p flags don't contain @ref Flag::UniformBuffers, + * @p materialCount and @p drawCount is ignored and the constructor + * behaves the same as @ref FlatGL(Flags). * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. */ - explicit FlatGL(Flags flags, UnsignedInt drawCount); + /** @todo this constructor will eventually need to have also joint + count, per-vertex weight count, view count for multiview and clip + plane count ... and putting them in arbitrary order next to each + other is too error-prone, so it needs some other solution + (accepting pairs of parameter type and value like in GL context + creation, e.g., which will probably need a new enum as reusing Flag + for this might be too confusing) */ + explicit FlatGL(Flags flags, UnsignedInt materialCount, UnsignedInt drawCount); #endif /** @@ -495,6 +507,18 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: Flags flags() const { return _flags; } #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Material count + * @m_since_latest + * + * Statically defined size of the @ref FlatMaterialUniform uniform + * buffer. Has use only if @ref Flag::UniformBuffers is set. + * @see @ref bindMaterialBuffer() + * @requires_gles30 Not defined on OpenGL ES 2.0 builds. + * @requires_webgl20 Not defined on WebGL 1.0 builds. + */ + UnsignedInt materialCount() const { return _materialCount; } + /** * @brief Draw count * @m_since_latest @@ -560,7 +584,8 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * coming from the @ref Color3 / @ref Color4 attribute. * * Expects that @ref Flag::UniformBuffers is not set, in that case fill - * @ref FlatDrawUniform::color and call @ref bindDrawBuffer() instead. + * @ref FlatMaterialUniform::color and call @ref bindMaterialBuffer() + * instead. * @see @ref bindTexture() */ FlatGL& setColor(const Magnum::Color4& color); @@ -578,8 +603,8 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * in classic OpenGL. * * Expects that @ref Flag::UniformBuffers is not set, in that case fill - * @ref FlatDrawUniform::alphaMask and call @ref bindDrawBuffer() - * instead. + * @ref FlatMaterialUniform::alphaMask and call + * @ref bindMaterialBuffer() instead. * @m_keywords{glAlphaFunc()} */ FlatGL& setAlphaMask(Float mask); @@ -648,7 +673,7 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * expected to contain @ref drawCount() instances of * @ref TransformationProjectionUniform2D / * @ref TransformationProjectionUniform3D. At the very least you need - * to call also @ref bindDrawBuffer(). + * to call also @ref bindDrawBuffer() and @ref bindMaterialBuffer(). * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. @@ -668,7 +693,8 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * Expects that @ref Flag::UniformBuffers is set. The buffer is * expected to contain @ref drawCount() instances of * @ref FlatDrawUniform. At the very least you need to call also - * @ref bindTransformationProjectionBuffer(). + * @ref bindTransformationProjectionBuffer() and + * @ref bindMaterialBuffer(). * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. @@ -700,6 +726,26 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: */ FlatGL& bindTextureTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + /** + * @brief Set a material uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref materialCount() instances of + * @ref FlatMaterialUniform. At the very least you need to call also + * @ref bindTransformationProjectionBuffer() and @ref bindDrawBuffer(). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + FlatGL& bindMaterialBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + FlatGL& bindMaterialBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + /** * @} */ @@ -735,7 +781,7 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: Flags _flags; #ifndef MAGNUM_TARGET_GLES2 - UnsignedInt _drawCount{}; + UnsignedInt _materialCount{}, _drawCount{}; #endif Int _transformationProjectionMatrixUniform{0}, _textureMatrixUniform{1}, diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index 974365108..b2581c749 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -86,7 +86,7 @@ struct FlatGLTest: GL::OpenGLTester { template void constructTextureTransformationNotTextured(); #ifndef MAGNUM_TARGET_GLES2 - template void constructUniformBuffersZeroDraws(); + template void constructUniformBuffersInvalid(); #endif #ifndef MAGNUM_TARGET_GLES2 @@ -210,17 +210,31 @@ constexpr struct { constexpr struct { const char* name; FlatGL2D::Flags flags; - UnsignedInt drawCount; + UnsignedInt materialCount, drawCount; } ConstructUniformBuffersData[]{ - {"classic fallback", {}, 1}, - {"", FlatGL2D::Flag::UniformBuffers, 1}, - /* SwiftShader has 256 uniform vectors at most, per-draw is 4+2 in 3D case - and 3+2 in 2D */ - {"multiple draws", FlatGL2D::Flag::UniformBuffers, 42}, - {"texture transformation", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1}, - {"alpha mask", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::AlphaMask, 1}, - {"object ID", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::ObjectId, 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, 42} + {"classic fallback", {}, 1, 1}, + {"", FlatGL2D::Flag::UniformBuffers, 1, 1}, + /* SwiftShader has 256 uniform vectors at most, per-draw is 4+1 in 3D case + and 3+1 in 2D, per-material 2 */ + {"multiple materials, draws", FlatGL2D::Flag::UniformBuffers, 8, 48}, + {"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} +}; +#endif + +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + FlatGL2D::Flags flags; + UnsignedInt materialCount, drawCount; + const char* message; +} ConstructUniformBuffersInvalidData[]{ + {"zero draws", FlatGL2D::Flag::UniformBuffers, 1, 0, + "draw count can't be zero"}, + {"zero materials", FlatGL2D::Flag::UniformBuffers, 0, 1, + "material count can't be zero"}, }; #endif @@ -282,30 +296,30 @@ constexpr struct { const char* expected2D; const char* expected3D; FlatGL2D::Flags flags; - UnsignedInt drawCount; + UnsignedInt materialCount, drawCount; UnsignedInt uniformIncrement; Float maxThreshold, meanThreshold; } RenderMultiData[] { {"bind with offset, colored", "multidraw2D.tga", "multidraw3D.tga", - {}, 1, 16, 0.0f, 0.0f}, + {}, 1, 1, 16, 0.0f, 0.0f}, {"bind with offset, textured", "multidraw-textured2D.tga", "multidraw-textured3D.tga", FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured, - 1, 16, + 1, 1, 16, /* Minor differences on ARM Mali */ 2.34f, 0.01f}, {"draw offset, colored", "multidraw2D.tga", "multidraw3D.tga", {}, - 3, 1, 0.0f, 0.0f}, + 2, 3, 1, 0.0f, 0.0f}, {"draw offset, textured", "multidraw-textured2D.tga", "multidraw-textured3D.tga", FlatGL2D::Flag::TextureTransformation|FlatGL2D::Flag::Textured, - 3, 1, + 2, 3, 1, /* Minor differences on ARM Mali */ 2.34f, 0.01f}, {"multidraw, colored", "multidraw2D.tga", "multidraw3D.tga", - FlatGL2D::Flag::MultiDraw, 3, 1, 0.0f, 0.0f}, + 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, - 3, 1, + 2, 3, 1, /* Minor differences on ARM Mali */ 2.34f, 0.01f} }; @@ -334,12 +348,16 @@ FlatGLTest::FlatGLTest() { #endif &FlatGLTest::constructTextureTransformationNotTextured<2>, - &FlatGLTest::constructTextureTransformationNotTextured<3>, - #ifndef MAGNUM_TARGET_GLES2 - &FlatGLTest::constructUniformBuffersZeroDraws<2>, - &FlatGLTest::constructUniformBuffersZeroDraws<3>, - #endif + &FlatGLTest::constructTextureTransformationNotTextured<3>}); + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({ + &FlatGLTest::constructUniformBuffersInvalid<2>, + &FlatGLTest::constructUniformBuffersInvalid<3>}, + Containers::arraySize(ConstructUniformBuffersInvalidData)); + #endif + + addTests({ #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::setUniformUniformBuffersEnabled<2>, &FlatGLTest::setUniformUniformBuffersEnabled<3>, @@ -557,8 +575,9 @@ template void FlatGLTest::constructUniformBuffers() { #endif } - FlatGL shader{data.flags, data.drawCount}; + FlatGL shader{data.flags, data.materialCount, data.drawCount}; CORRADE_COMPARE(shader.flags(), data.flags); + CORRADE_COMPARE(shader.materialCount(), data.materialCount); CORRADE_COMPARE(shader.drawCount(), data.drawCount); CORRADE_VERIFY(shader.id()); { @@ -602,7 +621,7 @@ template void FlatGLTest::constructMoveUniformBuffers() CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif - FlatGL a{FlatGL::Flag::UniformBuffers, 5}; + FlatGL a{FlatGL::Flag::UniformBuffers, 2, 5}; const GLuint id = a.id(); CORRADE_VERIFY(id); @@ -611,6 +630,7 @@ template void FlatGLTest::constructMoveUniformBuffers() FlatGL b{std::move(a)}; CORRADE_COMPARE(b.id(), id); CORRADE_COMPARE(b.flags(), FlatGL::Flag::UniformBuffers); + CORRADE_COMPARE(b.materialCount(), 2); CORRADE_COMPARE(b.drawCount(), 5); CORRADE_VERIFY(!a.id()); @@ -618,6 +638,7 @@ template void FlatGLTest::constructMoveUniformBuffers() c = std::move(b); CORRADE_COMPARE(c.id(), id); CORRADE_COMPARE(c.flags(), FlatGL::Flag::UniformBuffers); + CORRADE_COMPARE(c.materialCount(), 2); CORRADE_COMPARE(c.drawCount(), 5); CORRADE_VERIFY(!b.id()); } @@ -638,8 +659,10 @@ template void FlatGLTest::constructTextureTransformation } #ifndef MAGNUM_TARGET_GLES2 -template void FlatGLTest::constructUniformBuffersZeroDraws() { +template void FlatGLTest::constructUniformBuffersInvalid() { + auto&& data = ConstructUniformBuffersInvalidData[testCaseInstanceId()]; setTestCaseTemplateName(std::to_string(dimensions)); + setTestCaseDescription(data.name); #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -652,9 +675,9 @@ template void FlatGLTest::constructUniformBuffersZeroDra std::ostringstream out; Error redirectError{&out}; - FlatGL{FlatGL::Flag::UniformBuffers, 0}; - CORRADE_COMPARE(out.str(), - "Shaders::FlatGL: draw count can't be zero\n"); + FlatGL{data.flags, data.materialCount, data.drawCount}; + CORRADE_COMPARE(out.str(), Utility::formatString( + "Shaders::FlatGL: {}\n", data.message)); } #endif @@ -706,6 +729,8 @@ template void FlatGLTest::bindBufferUniformBuffersNotEna .bindDrawBuffer(buffer, 0, 16) .bindTextureTransformationBuffer(buffer) .bindTextureTransformationBuffer(buffer, 0, 16) + .bindMaterialBuffer(buffer) + .bindMaterialBuffer(buffer, 0, 16) .setDrawOffset(0); CORRADE_COMPARE(out.str(), "Shaders::FlatGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n" @@ -714,6 +739,8 @@ template void FlatGLTest::bindBufferUniformBuffersNotEna "Shaders::FlatGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::FlatGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::FlatGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::FlatGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::FlatGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::FlatGL::setDrawOffset(): the shader was not created with uniform buffers enabled\n"); } #endif @@ -829,7 +856,7 @@ template void FlatGLTest::setWrongDrawOffset() { std::ostringstream out; Error redirectError{&out}; - FlatGL{FlatGL::Flag::UniformBuffers, 5} + FlatGL{FlatGL::Flag::UniformBuffers, 2, 5} .setDrawOffset(5); CORRADE_COMPARE(out.str(), "Shaders::FlatGL::setDrawOffset(): draw offset 5 is out of bounds for 5 draws\n"); @@ -890,9 +917,13 @@ template void FlatGLTest::renderDefaults2D() { GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + FlatMaterialUniform{} + }}; shader .bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(circle); } #endif @@ -939,9 +970,13 @@ template void FlatGLTest::renderDefaults3D() { GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + FlatMaterialUniform{} + }}; shader .bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(sphere); } #endif @@ -991,11 +1026,15 @@ template void FlatGLTest::renderColored2D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + FlatMaterialUniform{} .setColor(0x9999ff_rgbf) }}; shader .bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(circle); } #endif @@ -1059,11 +1098,15 @@ template void FlatGLTest::renderColored3D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + FlatMaterialUniform{} .setColor(0x9999ff_rgbf) }}; shader .bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(sphere); } #endif @@ -1144,8 +1187,12 @@ template void FlatGLTest::renderSinglePixelTextured2D() { GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + FlatMaterialUniform{} + }}; shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(circle); } #endif @@ -1221,8 +1268,12 @@ template void FlatGLTest::renderSinglePixelTextured3D() { GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + FlatMaterialUniform{} + }}; shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(sphere); } #endif @@ -1302,16 +1353,20 @@ template void FlatGLTest::renderTextured2D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} - .setColor(0x9999ff_rgbf) }}; GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} .setTextureMatrix(data.textureTransformation) }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + FlatMaterialUniform{} + .setColor(0x9999ff_rgbf) + }}; if(data.flags & FlatGL2D::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(circle); } #endif @@ -1401,16 +1456,20 @@ template void FlatGLTest::renderTextured3D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} - .setColor(0x9999ff_rgbf) }}; GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} .setTextureMatrix(data.textureTransformation) }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + FlatMaterialUniform{} + .setColor(0x9999ff_rgbf) + }}; if(data.flags & FlatGL3D::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(sphere); } #endif @@ -1496,10 +1555,14 @@ template void FlatGLTest::renderVertexColor2D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + FlatMaterialUniform{} .setColor(0x9999ff_rgbf) }}; shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(circle); } #endif @@ -1590,10 +1653,14 @@ template void FlatGLTest::renderVertexColor3D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + FlatMaterialUniform{} .setColor(0x9999ff_rgbf) }}; shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(sphere); } #endif @@ -1685,11 +1752,15 @@ template void FlatGLTest::renderAlpha2D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + FlatMaterialUniform{} .setColor(0x9999ff_rgbf) .setAlphaMask(data.threshold) }}; shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(circle); } #endif @@ -1782,11 +1853,15 @@ template void FlatGLTest::renderAlpha3D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + FlatMaterialUniform{} .setColor(0x9999ff_rgbf) .setAlphaMask(data.threshold) }}; shader.bindTransformationProjectionBuffer(transformationProjectionUniform) - .bindDrawBuffer(drawUniform); + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform); /* For proper Z order draw back faces first and then front faces */ GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Front); @@ -1896,11 +1971,15 @@ template void FlatGLTest::renderObjectId2D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} - .setColor(0x9999ff_rgbf) .setObjectId(data.uniformId) }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + FlatMaterialUniform{} + .setColor(0x9999ff_rgbf) + }}; shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(circle); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); @@ -1990,11 +2069,15 @@ template void FlatGLTest::renderObjectId3D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} - .setColor(0x9999ff_rgbf) .setObjectId(data.uniformId) }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + FlatMaterialUniform{} + .setColor(0x9999ff_rgbf) + }}; shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(sphere); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); @@ -2127,15 +2210,19 @@ template void FlatGLTest::renderInstanced2D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} - .setColor(0xffff99_rgbf) }}; GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + FlatMaterialUniform{} + .setColor(0xffff99_rgbf) + }}; shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) .bindTextureTransformationBuffer(textureTransformationUniform) + .bindMaterialBuffer(materialUniform) .draw(circle); } #endif @@ -2251,15 +2338,19 @@ template void FlatGLTest::renderInstanced3D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} - .setColor(0xffff99_rgbf) }}; GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + FlatMaterialUniform{} + .setColor(0xffff99_rgbf) + }}; shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) .bindTextureTransformationBuffer(textureTransformationUniform) + .bindMaterialBuffer(materialUniform) .draw(sphere); } #endif @@ -2344,6 +2435,15 @@ void FlatGLTest::renderMulti2D() { The data.uniformIncrement is set high enough to ensure that, in the non-offset-bind case this value is 1. */ + Containers::Array materialData{data.uniformIncrement + 1}; + materialData[0*data.uniformIncrement] = FlatMaterialUniform{} + .setColor(data.flags & FlatGL2D::Flag::Textured ? + 0xffffff_rgbf : 0x0000ff_rgbf); + materialData[1*data.uniformIncrement] = FlatMaterialUniform{} + .setColor(data.flags & FlatGL2D::Flag::Textured ? + 0xffffff_rgbf : 0xff0000_rgbf); + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, materialData}; + Containers::Array transformationProjectionData{2*data.uniformIncrement + 1}; transformationProjectionData[0*data.uniformIncrement] = TransformationProjectionUniform2D{} .setTransformationProjectionMatrix( @@ -2384,26 +2484,28 @@ void FlatGLTest::renderMulti2D() { GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; Containers::Array drawData{2*data.uniformIncrement + 1}; + /* Material offsets are zero if we have single draw, as those are + done with UBO offset bindings instead. */ drawData[0*data.uniformIncrement] = FlatDrawUniform{} - .setColor(data.flags & FlatGL2D::Flag::Textured ? - 0xffffff_rgbf : 0xff0000_rgbf) + .setMaterialId(data.drawCount == 1 ? 0 : 1) .setObjectId(1211); drawData[1*data.uniformIncrement] = FlatDrawUniform{} - .setColor(data.flags & FlatGL2D::Flag::Textured ? - 0xffffff_rgbf : 0x0000ff_rgbf) + .setMaterialId(data.drawCount == 1 ? 0 : 0) .setObjectId(5627); drawData[2*data.uniformIncrement] = FlatDrawUniform{} - .setColor(data.flags & FlatGL2D::Flag::Textured ? - 0xffffff_rgbf : 0xff0000_rgbf) + .setMaterialId(data.drawCount == 1 ? 0 : 1) .setObjectId(36363); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - FlatGL2D shader{FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::ObjectId|data.flags, data.drawCount}; + 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, + 1*data.uniformIncrement*sizeof(FlatMaterialUniform), + sizeof(FlatMaterialUniform)); shader.bindTransformationProjectionBuffer(transformationProjectionUniform, 0*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), sizeof(TransformationProjectionUniform2D)); @@ -2416,6 +2518,9 @@ void FlatGLTest::renderMulti2D() { sizeof(TextureTransformationUniform)); shader.draw(circle); + shader.bindMaterialBuffer(materialUniform, + 0*data.uniformIncrement*sizeof(FlatMaterialUniform), + sizeof(FlatMaterialUniform)); shader.bindTransformationProjectionBuffer(transformationProjectionUniform, 1*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), sizeof(TransformationProjectionUniform2D)); @@ -2428,6 +2533,9 @@ void FlatGLTest::renderMulti2D() { sizeof(TextureTransformationUniform)); shader.draw(square); + shader.bindMaterialBuffer(materialUniform, + 1*data.uniformIncrement*sizeof(FlatMaterialUniform), + sizeof(FlatMaterialUniform)); shader.bindTransformationProjectionBuffer(transformationProjectionUniform, 2*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), sizeof(TransformationProjectionUniform2D)); @@ -2443,7 +2551,8 @@ void FlatGLTest::renderMulti2D() { /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform) - .bindDrawBuffer(drawUniform); + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform); if(data.flags & FlatGL2D::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); @@ -2565,6 +2674,15 @@ void FlatGLTest::renderMulti3D() { The data.uniformIncrement is set high enough to ensure that, in the non-offset-bind case this value is 1. */ + Containers::Array materialData{data.uniformIncrement + 1}; + materialData[0*data.uniformIncrement] = FlatMaterialUniform{} + .setColor(data.flags & FlatGL2D::Flag::Textured ? + 0xffffff_rgbf : 0x0000ff_rgbf); + materialData[1*data.uniformIncrement] = FlatMaterialUniform{} + .setColor(data.flags & FlatGL2D::Flag::Textured ? + 0xffffff_rgbf : 0xff0000_rgbf); + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, materialData}; + Containers::Array transformationProjectionData{2*data.uniformIncrement + 1}; transformationProjectionData[0*data.uniformIncrement] = TransformationProjectionUniform3D{} .setTransformationProjectionMatrix( @@ -2611,26 +2729,28 @@ void FlatGLTest::renderMulti3D() { GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; Containers::Array drawData{2*data.uniformIncrement + 1}; + /* Material offsets are zero if we have single draw, as those are done with + UBO offset bindings instead. */ drawData[0*data.uniformIncrement] = FlatDrawUniform{} - .setColor(data.flags & FlatGL3D::Flag::Textured ? - 0xffffff_rgbf : 0xff0000_rgbf) + .setMaterialId(data.drawCount == 1 ? 0 : 1) .setObjectId(1211); drawData[1*data.uniformIncrement] = FlatDrawUniform{} - .setColor(data.flags & FlatGL3D::Flag::Textured ? - 0xffffff_rgbf : 0x0000ff_rgbf) + .setMaterialId(data.drawCount == 1 ? 0 : 0) .setObjectId(5627); drawData[2*data.uniformIncrement] = FlatDrawUniform{} - .setColor(data.flags & FlatGL3D::Flag::Textured ? - 0xffffff_rgbf : 0xff0000_rgbf) + .setMaterialId(data.drawCount == 1 ? 0 : 1) .setObjectId(36363); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - FlatGL3D shader{FlatGL3D::Flag::UniformBuffers|FlatGL3D::Flag::ObjectId|data.flags, data.drawCount}; + 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, + 1*data.uniformIncrement*sizeof(FlatMaterialUniform), + sizeof(FlatMaterialUniform)); shader.bindTransformationProjectionBuffer(transformationProjectionUniform, 0*data.uniformIncrement*sizeof(TransformationProjectionUniform3D), sizeof(TransformationProjectionUniform3D)); @@ -2643,6 +2763,9 @@ void FlatGLTest::renderMulti3D() { sizeof(TextureTransformationUniform)); shader.draw(sphere); + shader.bindMaterialBuffer(materialUniform, + 0*data.uniformIncrement*sizeof(FlatMaterialUniform), + sizeof(FlatMaterialUniform)); shader.bindTransformationProjectionBuffer(transformationProjectionUniform, 1*data.uniformIncrement*sizeof(TransformationProjectionUniform3D), sizeof(TransformationProjectionUniform3D)); @@ -2655,6 +2778,9 @@ void FlatGLTest::renderMulti3D() { sizeof(TextureTransformationUniform)); shader.draw(plane); + shader.bindMaterialBuffer(materialUniform, + 1*data.uniformIncrement*sizeof(FlatMaterialUniform), + sizeof(FlatMaterialUniform)); shader.bindTransformationProjectionBuffer(transformationProjectionUniform, 2*data.uniformIncrement*sizeof(TransformationProjectionUniform3D), sizeof(TransformationProjectionUniform3D)); @@ -2670,7 +2796,8 @@ void FlatGLTest::renderMulti3D() { /* Otherwise using the draw offset / multidraw */ } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform) - .bindDrawBuffer(drawUniform); + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform); if(data.flags & FlatGL3D::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); diff --git a/src/Magnum/Shaders/Test/FlatTest.cpp b/src/Magnum/Shaders/Test/FlatTest.cpp index 3ab604d65..206c04f7d 100644 --- a/src/Magnum/Shaders/Test/FlatTest.cpp +++ b/src/Magnum/Shaders/Test/FlatTest.cpp @@ -38,14 +38,25 @@ struct FlatTest: TestSuite::Tester { void drawUniformConstructDefault(); void drawUniformConstructNoInit(); void drawUniformSetters(); + void drawUniformMaterialIdPacking(); + + void materialUniformConstructDefault(); + void materialUniformConstructNoInit(); + void materialUniformSetters(); }; FlatTest::FlatTest() { addTests({&FlatTest::uniformSizeAlignment, + &FlatTest::uniformSizeAlignment, &FlatTest::drawUniformConstructDefault, &FlatTest::drawUniformConstructNoInit, - &FlatTest::drawUniformSetters}); + &FlatTest::drawUniformSetters, + &FlatTest::drawUniformMaterialIdPacking, + + &FlatTest::materialUniformConstructDefault, + &FlatTest::materialUniformConstructNoInit, + &FlatTest::materialUniformSetters}); } using namespace Math::Literals; @@ -54,6 +65,9 @@ template struct UniformTraits; template<> struct UniformTraits { static const char* name() { return "FlatDrawUniform"; } }; +template<> struct UniformTraits { + static const char* name() { return "FlatMaterialUniform"; } +}; template void FlatTest::uniformSizeAlignment() { setTestCaseTemplateName(UniformTraits::name()); @@ -72,21 +86,17 @@ template void FlatTest::uniformSizeAlignment() { void FlatTest::drawUniformConstructDefault() { FlatDrawUniform a; FlatDrawUniform b{DefaultInit}; - CORRADE_COMPARE(a.color, 0xffffffff_rgbaf); - CORRADE_COMPARE(b.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(a.materialId, 0); + CORRADE_COMPARE(b.materialId, 0); CORRADE_COMPARE(a.objectId, 0); CORRADE_COMPARE(b.objectId, 0); - CORRADE_COMPARE(a.alphaMask, 0.5f); - CORRADE_COMPARE(b.alphaMask, 0.5f); constexpr FlatDrawUniform ca; constexpr FlatDrawUniform cb{DefaultInit}; - CORRADE_COMPARE(ca.color, 0xffffffff_rgbaf); - CORRADE_COMPARE(cb.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(ca.materialId, 0); + CORRADE_COMPARE(cb.materialId, 0); CORRADE_COMPARE(ca.objectId, 0); CORRADE_COMPARE(cb.objectId, 0); - CORRADE_COMPARE(ca.alphaMask, 0.5f); - CORRADE_COMPARE(cb.alphaMask, 0.5f); CORRADE_VERIFY(std::is_nothrow_default_constructible::value); CORRADE_VERIFY(std::is_nothrow_constructible::value); @@ -98,16 +108,16 @@ void FlatTest::drawUniformConstructDefault() { void FlatTest::drawUniformConstructNoInit() { /* Testing only some fields, should be enough */ FlatDrawUniform a; - a.color = 0x354565fc_rgbaf; - a.alphaMask = 0.7f; + a.materialId = 5; + a.objectId = 7; new(&a) FlatDrawUniform{NoInit}; { #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); #endif - CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); - CORRADE_COMPARE(a.alphaMask, 0.7f); + CORRADE_COMPARE(a.materialId, 5); + CORRADE_COMPARE(a.objectId, 7); } CORRADE_VERIFY(std::is_nothrow_constructible::value); @@ -118,11 +128,68 @@ void FlatTest::drawUniformConstructNoInit() { void FlatTest::drawUniformSetters() { FlatDrawUniform a; + a.setMaterialId(5) + .setObjectId(7); + CORRADE_COMPARE(a.materialId, 5); + CORRADE_COMPARE(a.objectId, 7); +} + +void FlatTest::drawUniformMaterialIdPacking() { + FlatDrawUniform a; + a.setMaterialId(13765); + /* materialId should be right at the beginning, in the low 16 bits on both + LE and BE */ + CORRADE_COMPARE(reinterpret_cast(&a)[0] & 0xffff, 13765); +} + +void FlatTest::materialUniformConstructDefault() { + FlatMaterialUniform a; + FlatMaterialUniform b{DefaultInit}; + CORRADE_COMPARE(a.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(b.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(a.alphaMask, 0.5f); + CORRADE_COMPARE(b.alphaMask, 0.5f); + + constexpr FlatMaterialUniform ca; + constexpr FlatMaterialUniform cb{DefaultInit}; + CORRADE_COMPARE(ca.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(cb.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(ca.alphaMask, 0.5f); + CORRADE_COMPARE(cb.alphaMask, 0.5f); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void FlatTest::materialUniformConstructNoInit() { + /* Testing only some fields, should be enough */ + FlatMaterialUniform a; + a.color = 0x354565fc_rgbaf; + a.alphaMask = 0.7f; + + new(&a) FlatMaterialUniform{NoInit}; + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); + CORRADE_COMPARE(a.alphaMask, 0.7f); + } + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void FlatTest::materialUniformSetters() { + FlatMaterialUniform a; a.setColor(0x354565fc_rgbaf) - .setObjectId(7) .setAlphaMask(0.7f); CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); - CORRADE_COMPARE(a.objectId, 7); CORRADE_COMPARE(a.alphaMask, 0.7f); } diff --git a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp index 814059a16..15cd1f835 100644 --- a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp +++ b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp @@ -130,27 +130,27 @@ constexpr std::size_t BenchmarkRepeats{4}; const struct { const char* name; FlatGL2D::Flags flags; - UnsignedInt drawCount; + UnsignedInt materialCount, drawCount; } FlatData[] { - {"", {}, 1}, - {"vertex color", FlatGL2D::Flag::VertexColor, 1}, + {"", {}, 1, 1}, + {"vertex color", FlatGL2D::Flag::VertexColor, 1, 1}, #ifndef MAGNUM_TARGET_GLES2 - {"object ID", FlatGL2D::Flag::ObjectId, 1}, + {"object ID", FlatGL2D::Flag::ObjectId, 1, 1}, #endif - {"textured", FlatGL2D::Flag::Textured, 1}, - {"textured + alpha mask", FlatGL2D::Flag::Textured|FlatGL2D::Flag::AlphaMask, 1}, - {"texture transformation", FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1}, - {"instanced transformation", FlatGL2D::Flag::InstancedTransformation, 1}, - {"instanced transformation + color", FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::VertexColor, 1}, + {"textured", FlatGL2D::Flag::Textured, 1, 1}, + {"textured + alpha mask", FlatGL2D::Flag::Textured|FlatGL2D::Flag::AlphaMask, 1, 1}, + {"texture transformation", FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1, 1}, + {"instanced transformation", FlatGL2D::Flag::InstancedTransformation, 1, 1}, + {"instanced transformation + color", FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::VertexColor, 1, 1}, #ifndef MAGNUM_TARGET_GLES2 - {"instanced transformation + object ID", FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::InstancedObjectId, 1}, + {"instanced transformation + object ID", FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::InstancedObjectId, 1, 1}, #endif - {"instanced transformation + texture offset", FlatGL2D::Flag::Textured|FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::InstancedTextureOffset, 1}, + {"instanced transformation + texture offset", FlatGL2D::Flag::Textured|FlatGL2D::Flag::InstancedTransformation|FlatGL2D::Flag::InstancedTextureOffset, 1, 1}, #ifndef MAGNUM_TARGET_GLES2 - {"UBO single", FlatGL2D::Flag::UniformBuffers, 1}, - {"UBO single, texture transformation", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1}, - {"UBO multi", FlatGL2D::Flag::UniformBuffers, 128}, - {"multidraw", FlatGL2D::Flag::MultiDraw, 128}, + {"UBO single", FlatGL2D::Flag::UniformBuffers, 1, 1}, + {"UBO single, texture transformation", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1, 1}, + {"UBO multi", FlatGL2D::Flag::UniformBuffers, 32, 128}, + {"multidraw", FlatGL2D::Flag::MultiDraw, 32, 128}, #endif }; @@ -541,7 +541,7 @@ template void ShadersGLBenchmark::flat() { FlatGL shader{data.flags #ifndef MAGNUM_TARGET_GLES2 - , data.drawCount + , data.materialCount, data.drawCount #endif }; @@ -549,13 +549,16 @@ template void ShadersGLBenchmark::flat() { GL::Buffer transformationProjectionUniform{NoCreate}; GL::Buffer drawUniform{NoCreate}; GL::Buffer textureTransformationUniform{NoCreate}; + GL::Buffer materialUniform{NoCreate}; if(data.flags & FlatGL2D::Flag::UniformBuffers) { transformationProjectionUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array::TransformationProjection>{data.drawCount}}; - Containers::Array drawData{data.drawCount}; - drawData[0].setAlphaMask(0.0f); - drawUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, drawData}; + drawUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; + Containers::Array materialData{data.materialCount}; + materialData[0].setAlphaMask(0.0f); + materialUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, materialData}; shader.bindTransformationProjectionBuffer(transformationProjectionUniform) - .bindDrawBuffer(drawUniform); + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform); if(data.flags & FlatGL2D::Flag::TextureTransformation) { textureTransformationUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; shader.bindTextureTransformationBuffer(textureTransformationUniform); From 807768138877e18a54415bb651ef82a83929cb12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 27 May 2021 18:09:55 +0200 Subject: [PATCH 39/93] Shaders: supply Vector material information in a separate UBO as well. So it's all having the same workflow. This one results in even more saved UBO slots per-draw than in the case of Flat, and the slowdown on Intel is as bad as expected. --- .../Shaders/Test/ShadersGLBenchmark.cpp | 23 +-- src/Magnum/Shaders/Test/VectorGLTest.cpp | 167 +++++++++++++----- src/Magnum/Shaders/Test/VectorTest.cpp | 91 ++++++++-- src/Magnum/Shaders/Vector.frag | 24 ++- src/Magnum/Shaders/Vector.h | 145 +++++++++++---- src/Magnum/Shaders/VectorGL.cpp | 32 +++- src/Magnum/Shaders/VectorGL.h | 65 +++++-- 7 files changed, 423 insertions(+), 124 deletions(-) diff --git a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp index 15cd1f835..dc2cf2e5f 100644 --- a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp +++ b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp @@ -204,16 +204,16 @@ const struct { const struct { const char* name; VectorGL2D::Flags flags; - UnsignedInt drawCount; + UnsignedInt materialCount, drawCount; } VectorData[] { - {"", {}, 1}, - {"texture transformation", VectorGL2D::Flag::TextureTransformation, 1}, + {"", {}, 1, 1}, + {"texture transformation", VectorGL2D::Flag::TextureTransformation, 1, 1}, #ifndef MAGNUM_TARGET_GLES2 - {"UBO single", VectorGL2D::Flag::UniformBuffers, 1}, - {"UBO single, texture transformation", VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, 1}, - {"UBO multi", VectorGL2D::Flag::UniformBuffers, 128}, - {"UBO multi, texture transformation", VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, 128}, - {"multidraw", VectorGL2D::Flag::MultiDraw, 128}, + {"UBO single", VectorGL2D::Flag::UniformBuffers, 1, 1}, + {"UBO single, texture transformation", VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, 1, 1}, + {"UBO multi", VectorGL2D::Flag::UniformBuffers, 32, 128}, + {"UBO multi, texture transformation", VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, 32, 128}, + {"multidraw", VectorGL2D::Flag::MultiDraw, 32, 128}, #endif }; @@ -826,7 +826,7 @@ template void ShadersGLBenchmark::vector() { VectorGL shader{data.flags #ifndef MAGNUM_TARGET_GLES2 - , data.drawCount + , data.materialCount, data.drawCount #endif }; shader.bindVectorTexture(_textureWhite); @@ -835,11 +835,14 @@ template void ShadersGLBenchmark::vector() { GL::Buffer transformationProjectionUniform{NoCreate}; GL::Buffer drawUniform{NoCreate}; GL::Buffer textureTransformationUniform{NoCreate}; + GL::Buffer materialUniform{NoCreate}; if(data.flags & VectorGL2D::Flag::UniformBuffers) { transformationProjectionUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array::TransformationProjection>{data.drawCount}}; drawUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; + materialUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.materialCount}}; shader.bindTransformationProjectionBuffer(transformationProjectionUniform) - .bindDrawBuffer(drawUniform); + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform); if(data.flags & VectorGL2D::Flag::TextureTransformation) { textureTransformationUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} diff --git a/src/Magnum/Shaders/Test/VectorGLTest.cpp b/src/Magnum/Shaders/Test/VectorGLTest.cpp index 1f5260c95..ce9a77d21 100644 --- a/src/Magnum/Shaders/Test/VectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VectorGLTest.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "Magnum/Image.h" #include "Magnum/ImageView.h" @@ -85,7 +86,7 @@ struct VectorGLTest: GL::OpenGLTester { #endif #ifndef MAGNUM_TARGET_GLES2 - template void constructUniformBuffersZeroDraws(); + template void constructUniformBuffersInvalid(); #endif #ifndef MAGNUM_TARGET_GLES2 @@ -165,15 +166,29 @@ constexpr struct { constexpr struct { const char* name; VectorGL2D::Flags flags; - UnsignedInt drawCount; + UnsignedInt materialCount, drawCount; } ConstructUniformBuffersData[]{ - {"classic fallback", {}, 1}, - {"", VectorGL2D::Flag::UniformBuffers, 1}, - {"texture transformation", VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, 1}, - /* SwiftShader has 256 uniform vectors at most, per-draw is 4+3 in 3D case - and 3+3 in 2D */ - {"multiple draws", VectorGL2D::Flag::UniformBuffers, 36}, - {"multidraw with all the things", VectorGL2D::Flag::MultiDraw|VectorGL2D::Flag::TextureTransformation, 36} + {"classic fallback", {}, 1, 1}, + {"", VectorGL2D::Flag::UniformBuffers, 1, 1}, + {"texture transformation", VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, 1, 1}, + /* SwiftShader has 256 uniform vectors at most, per-draw is 4+1 in 3D case + and 3+1 in 2D, per-material 3 */ + {"multiple materials, draws", VectorGL2D::Flag::UniformBuffers, 15, 42}, + {"multidraw with all the things", VectorGL2D::Flag::MultiDraw|VectorGL2D::Flag::TextureTransformation, 15, 42} +}; +#endif + +#ifndef MAGNUM_TARGET_GLES2 +constexpr struct { + const char* name; + VectorGL2D::Flags flags; + UnsignedInt materialCount, drawCount; + const char* message; +} ConstructUniformBuffersInvalidData[]{ + {"zero draws", VectorGL2D::Flag::UniformBuffers, 1, 0, + "draw count can't be zero"}, + {"zero materials", VectorGL2D::Flag::UniformBuffers, 0, 1, + "material count can't be zero"}, }; #endif @@ -200,20 +215,20 @@ constexpr struct { const char* expected2D; const char* expected3D; VectorGL2D::Flags flags; - UnsignedInt drawCount; + UnsignedInt materialCount, drawCount; UnsignedInt uniformIncrement; Float maxThreshold, meanThreshold; } RenderMultiData[] { {"bind with offset", "multidraw2D.tga", "multidraw3D.tga", - {}, 1, 16, + {}, 1, 1, 16, /* Minor differences on ARM Mali */ 1.34f, 0.02f}, {"draw offset", "multidraw2D.tga", "multidraw3D.tga", - {}, 3, 1, + {}, 2, 3, 1, /* Minor differences on ARM Mali */ 1.34f, 0.02f}, {"multidraw", "multidraw2D.tga", "multidraw3D.tga", - VectorGL2D::Flag::MultiDraw, 3, 1, + VectorGL2D::Flag::MultiDraw, 2, 3, 1, /* Minor differences on ARM Mali */ 1.34f, 0.02f}, }; @@ -240,12 +255,16 @@ VectorGLTest::VectorGLTest() { &VectorGLTest::constructMoveUniformBuffers<2>, &VectorGLTest::constructMoveUniformBuffers<3>, #endif + }); - #ifndef MAGNUM_TARGET_GLES2 - &VectorGLTest::constructUniformBuffersZeroDraws<2>, - &VectorGLTest::constructUniformBuffersZeroDraws<3>, - #endif + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({ + &VectorGLTest::constructUniformBuffersInvalid<2>, + &VectorGLTest::constructUniformBuffersInvalid<3>}, + Containers::arraySize(ConstructUniformBuffersInvalidData)); + #endif + addTests({ #ifndef MAGNUM_TARGET_GLES2 &VectorGLTest::setUniformUniformBuffersEnabled<2>, &VectorGLTest::setUniformUniformBuffersEnabled<3>, @@ -369,7 +388,7 @@ template void VectorGLTest::constructUniformBuffers() { #endif } - VectorGL shader{data.flags, data.drawCount}; + VectorGL shader{data.flags, data.materialCount, data.drawCount}; CORRADE_COMPARE(shader.flags(), data.flags); CORRADE_COMPARE(shader.drawCount(), data.drawCount); CORRADE_VERIFY(shader.id()); @@ -414,7 +433,7 @@ template void VectorGLTest::constructMoveUniformBuffers( CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif - VectorGL a{VectorGL::Flag::UniformBuffers, 5}; + VectorGL a{VectorGL::Flag::UniformBuffers, 2, 5}; const GLuint id = a.id(); CORRADE_VERIFY(id); @@ -423,6 +442,7 @@ template void VectorGLTest::constructMoveUniformBuffers( VectorGL b{std::move(a)}; CORRADE_COMPARE(b.id(), id); CORRADE_COMPARE(b.flags(), VectorGL::Flag::UniformBuffers); + CORRADE_COMPARE(b.materialCount(), 2); CORRADE_COMPARE(b.drawCount(), 5); CORRADE_VERIFY(!a.id()); @@ -430,14 +450,17 @@ template void VectorGLTest::constructMoveUniformBuffers( c = std::move(b); CORRADE_COMPARE(c.id(), id); CORRADE_COMPARE(c.flags(), VectorGL::Flag::UniformBuffers); + CORRADE_COMPARE(c.materialCount(), 2); CORRADE_COMPARE(c.drawCount(), 5); CORRADE_VERIFY(!b.id()); } #endif #ifndef MAGNUM_TARGET_GLES2 -template void VectorGLTest::constructUniformBuffersZeroDraws() { +template void VectorGLTest::constructUniformBuffersInvalid() { + auto&& data = ConstructUniformBuffersInvalidData[testCaseInstanceId()]; setTestCaseTemplateName(std::to_string(dimensions)); + setTestCaseDescription(data.name); #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -450,9 +473,9 @@ template void VectorGLTest::constructUniformBuffersZeroD std::ostringstream out; Error redirectError{&out}; - VectorGL{VectorGL::Flag::UniformBuffers, 0}; - CORRADE_COMPARE(out.str(), - "Shaders::VectorGL: draw count can't be zero\n"); + VectorGL{data.flags, data.materialCount, data.drawCount}; + CORRADE_COMPARE(out.str(), Utility::formatString( + "Shaders::VectorGL: {}\n", data.message)); } #endif @@ -502,6 +525,8 @@ template void VectorGLTest::bindBufferUniformBuffersNotE .bindDrawBuffer(buffer, 0, 16) .bindTextureTransformationBuffer(buffer) .bindTextureTransformationBuffer(buffer, 0, 16) + .bindMaterialBuffer(buffer) + .bindMaterialBuffer(buffer, 0, 16) .setDrawOffset(0); CORRADE_COMPARE(out.str(), "Shaders::VectorGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n" @@ -510,6 +535,8 @@ template void VectorGLTest::bindBufferUniformBuffersNotE "Shaders::VectorGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::VectorGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::VectorGL::bindTextureTransformationBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::VectorGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" + "Shaders::VectorGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n" "Shaders::VectorGL::setDrawOffset(): the shader was not created with uniform buffers enabled\n"); } #endif @@ -572,7 +599,7 @@ template void VectorGLTest::setWrongDrawOffset() { std::ostringstream out; Error redirectError{&out}; - VectorGL{VectorGL::Flag::UniformBuffers, 5} + VectorGL{VectorGL::Flag::UniformBuffers, 2, 5} .setDrawOffset(5); CORRADE_COMPARE(out.str(), "Shaders::VectorGL::setDrawOffset(): draw offset 5 is out of bounds for 5 draws\n"); @@ -665,8 +692,12 @@ template void VectorGLTest::renderDefaults2D() { GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { VectorDrawUniform{} }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + VectorMaterialUniform{} + }}; shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(square); } #endif @@ -740,8 +771,12 @@ template void VectorGLTest::renderDefaults3D() { GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { VectorDrawUniform{} }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + VectorMaterialUniform{} + }}; shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(plane); } #endif @@ -829,17 +864,21 @@ template void VectorGLTest::render2D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { VectorDrawUniform{} - .setBackgroundColor(data.backgroundColor) - .setColor(data.color) }}; GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} .setTextureMatrix(data.textureTransformation) }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + VectorMaterialUniform{} + .setBackgroundColor(data.backgroundColor) + .setColor(data.color) + }}; if(data.flags & VectorGL2D::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(square); } #endif @@ -934,17 +973,21 @@ template void VectorGLTest::render3D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { VectorDrawUniform{} - .setBackgroundColor(data.backgroundColor) - .setColor(data.color) }}; GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} .setTextureMatrix(data.textureTransformation) }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + VectorMaterialUniform{} + .setBackgroundColor(data.backgroundColor) + .setColor(data.color) + }}; if(data.flags & VectorGL3D::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) .draw(plane); } #endif @@ -1031,6 +1074,15 @@ void VectorGLTest::renderMulti2D() { The data.uniformIncrement is set high enough to ensure that, in the non-offset-bind case this value is 1. */ + Containers::Array materialData{data.uniformIncrement + 1}; + materialData[0*data.uniformIncrement] = VectorMaterialUniform{} + .setColor(0xff0000_rgbf) + .setBackgroundColor(0xffcccc_rgbf); + materialData[1*data.uniformIncrement] = VectorMaterialUniform{} + .setColor(0x00ff00_rgbf) + .setBackgroundColor(0xccffcc_rgbf); + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, materialData}; + Containers::Array transformationProjectionData{2*data.uniformIncrement + 1}; transformationProjectionData[0*data.uniformIncrement] = TransformationProjectionUniform2D{} .setTransformationProjectionMatrix( @@ -1069,22 +1121,24 @@ void VectorGLTest::renderMulti2D() { GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; Containers::Array drawData{2*data.uniformIncrement + 1}; + /* Material offsets are zero if we have single draw, as those are done with + UBO offset bindings instead. */ drawData[0*data.uniformIncrement] = VectorDrawUniform{} - .setColor(0x00ff00_rgbf) - .setBackgroundColor(0xccffcc_rgbf); + .setMaterialId(data.drawCount == 1 ? 0 : 1); drawData[1*data.uniformIncrement] = VectorDrawUniform{} - .setColor(0xff0000_rgbf) - .setBackgroundColor(0xffcccc_rgbf); + .setMaterialId(data.drawCount == 1 ? 0 : 0); drawData[2*data.uniformIncrement] = VectorDrawUniform{} - .setColor(0x00ff00_rgbf) - .setBackgroundColor(0xccffcc_rgbf); + .setMaterialId(data.drawCount == 1 ? 0 : 1); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - VectorGL2D shader{VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation|data.flags, data.drawCount}; + VectorGL2D shader{VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation|data.flags, data.materialCount, data.drawCount}; shader.bindVectorTexture(vector); /* Just one draw, rebinding UBOs each time */ if(data.drawCount == 1) { + shader.bindMaterialBuffer(materialUniform, + 1*data.uniformIncrement*sizeof(VectorMaterialUniform), + sizeof(VectorMaterialUniform)); shader.bindTransformationProjectionBuffer(transformationProjectionUniform, 0*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), sizeof(TransformationProjectionUniform2D)); @@ -1096,6 +1150,9 @@ void VectorGLTest::renderMulti2D() { sizeof(TextureTransformationUniform)); shader.draw(circle); + shader.bindMaterialBuffer(materialUniform, + 0*data.uniformIncrement*sizeof(VectorMaterialUniform), + sizeof(VectorMaterialUniform)); shader.bindTransformationProjectionBuffer(transformationProjectionUniform, 1*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), sizeof(TransformationProjectionUniform2D)); @@ -1107,6 +1164,9 @@ void VectorGLTest::renderMulti2D() { sizeof(TextureTransformationUniform)); shader.draw(square); + shader.bindMaterialBuffer(materialUniform, + 1*data.uniformIncrement*sizeof(VectorMaterialUniform), + sizeof(VectorMaterialUniform)); shader.bindTransformationProjectionBuffer(transformationProjectionUniform, 2*data.uniformIncrement*sizeof(TransformationProjectionUniform2D), sizeof(TransformationProjectionUniform2D)); @@ -1122,7 +1182,8 @@ void VectorGLTest::renderMulti2D() { } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) - .bindTextureTransformationBuffer(textureTransformationUniform); + .bindTextureTransformationBuffer(textureTransformationUniform) + .bindMaterialBuffer(materialUniform); if(data.flags >= VectorGL2D::Flag::MultiDraw) shader.draw({circle, square, triangle}); @@ -1209,6 +1270,15 @@ void VectorGLTest::renderMulti3D() { The data.uniformIncrement is set high enough to ensure that, in the non-offset-bind case this value is 1. */ + Containers::Array materialData{data.uniformIncrement + 1}; + materialData[0*data.uniformIncrement] = VectorMaterialUniform{} + .setColor(0xff0000_rgbf) + .setBackgroundColor(0xffcccc_rgbf); + materialData[1*data.uniformIncrement] = VectorMaterialUniform{} + .setColor(0x00ff00_rgbf) + .setBackgroundColor(0xccffcc_rgbf); + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, materialData}; + Containers::Array transformationProjectionData{2*data.uniformIncrement + 1}; transformationProjectionData[0*data.uniformIncrement] = TransformationProjectionUniform3D{} .setTransformationProjectionMatrix( @@ -1252,22 +1322,24 @@ void VectorGLTest::renderMulti3D() { GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, textureTransformationData}; Containers::Array drawData{2*data.uniformIncrement + 1}; + /* Material offsets are zero if we have single draw, as those are done with + UBO offset bindings instead. */ drawData[0*data.uniformIncrement] = VectorDrawUniform{} - .setColor(0x00ff00_rgbf) - .setBackgroundColor(0xccffcc_rgbf); + .setMaterialId(data.drawCount == 1 ? 0 : 1); drawData[1*data.uniformIncrement] = VectorDrawUniform{} - .setColor(0xff0000_rgbf) - .setBackgroundColor(0xffcccc_rgbf); + .setMaterialId(data.drawCount == 1 ? 0 : 0); drawData[2*data.uniformIncrement] = VectorDrawUniform{} - .setColor(0x00ff00_rgbf) - .setBackgroundColor(0xccffcc_rgbf); + .setMaterialId(data.drawCount == 1 ? 0 : 1); GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData}; - VectorGL3D shader{VectorGL3D::Flag::UniformBuffers|VectorGL3D::Flag::TextureTransformation|data.flags, data.drawCount}; + VectorGL3D shader{VectorGL3D::Flag::UniformBuffers|VectorGL3D::Flag::TextureTransformation|data.flags, data.materialCount, data.drawCount}; shader.bindVectorTexture(vector); /* Just one draw, rebinding UBOs each time */ if(data.drawCount == 1) { + shader.bindMaterialBuffer(materialUniform, + 1*data.uniformIncrement*sizeof(VectorMaterialUniform), + sizeof(VectorMaterialUniform)); shader.bindTransformationProjectionBuffer(transformationProjectionUniform, 0*data.uniformIncrement*sizeof(TransformationProjectionUniform3D), sizeof(TransformationProjectionUniform3D)); @@ -1279,6 +1351,9 @@ void VectorGLTest::renderMulti3D() { sizeof(TextureTransformationUniform)); shader.draw(sphere); + shader.bindMaterialBuffer(materialUniform, + 0*data.uniformIncrement*sizeof(VectorMaterialUniform), + sizeof(VectorMaterialUniform)); shader.bindTransformationProjectionBuffer(transformationProjectionUniform, 1*data.uniformIncrement*sizeof(TransformationUniform3D), sizeof(TransformationUniform3D)); @@ -1290,6 +1365,9 @@ void VectorGLTest::renderMulti3D() { sizeof(TextureTransformationUniform)); shader.draw(plane); + shader.bindMaterialBuffer(materialUniform, + 1*data.uniformIncrement*sizeof(VectorMaterialUniform), + sizeof(VectorMaterialUniform)); shader.bindTransformationProjectionBuffer(transformationProjectionUniform, 2*data.uniformIncrement*sizeof(TransformationUniform3D), sizeof(TransformationUniform3D)); @@ -1305,7 +1383,8 @@ void VectorGLTest::renderMulti3D() { } else { shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) - .bindTextureTransformationBuffer(textureTransformationUniform); + .bindTextureTransformationBuffer(textureTransformationUniform) + .bindMaterialBuffer(materialUniform); if(data.flags >= VectorGL3D::Flag::MultiDraw) shader.draw({sphere, plane, cone}); diff --git a/src/Magnum/Shaders/Test/VectorTest.cpp b/src/Magnum/Shaders/Test/VectorTest.cpp index fffb94115..12985d5ba 100644 --- a/src/Magnum/Shaders/Test/VectorTest.cpp +++ b/src/Magnum/Shaders/Test/VectorTest.cpp @@ -38,14 +38,25 @@ struct VectorTest: TestSuite::Tester { void drawUniformConstructDefault(); void drawUniformConstructNoInit(); void drawUniformSetters(); + void drawUniformMaterialIdPacking(); + + void materialUniformConstructDefault(); + void materialUniformConstructNoInit(); + void materialUniformSetters(); }; VectorTest::VectorTest() { addTests({&VectorTest::uniformSizeAlignment, + &VectorTest::uniformSizeAlignment, &VectorTest::drawUniformConstructDefault, &VectorTest::drawUniformConstructNoInit, - &VectorTest::drawUniformSetters}); + &VectorTest::drawUniformSetters, + &VectorTest::drawUniformMaterialIdPacking, + + &VectorTest::materialUniformConstructDefault, + &VectorTest::materialUniformConstructNoInit, + &VectorTest::materialUniformSetters}); } using namespace Math::Literals; @@ -54,6 +65,9 @@ template struct UniformTraits; template<> struct UniformTraits { static const char* name() { return "VectorDrawUniform"; } }; +template<> struct UniformTraits { + static const char* name() { return "VectorMaterialUniform"; } +}; template void VectorTest::uniformSizeAlignment() { setTestCaseTemplateName(UniformTraits::name()); @@ -72,17 +86,13 @@ template void VectorTest::uniformSizeAlignment() { void VectorTest::drawUniformConstructDefault() { VectorDrawUniform a; VectorDrawUniform b{DefaultInit}; - CORRADE_COMPARE(a.color, 0xffffffff_rgbaf); - CORRADE_COMPARE(b.color, 0xffffffff_rgbaf); - CORRADE_COMPARE(a.backgroundColor, 0x00000000_rgbaf); - CORRADE_COMPARE(b.backgroundColor, 0x00000000_rgbaf); + CORRADE_COMPARE(a.materialId, 0); + CORRADE_COMPARE(b.materialId, 0); constexpr VectorDrawUniform ca; constexpr VectorDrawUniform cb{DefaultInit}; - CORRADE_COMPARE(ca.color, 0xffffffff_rgbaf); - CORRADE_COMPARE(cb.color, 0xffffffff_rgbaf); - CORRADE_COMPARE(ca.backgroundColor, 0x00000000_rgbaf); - CORRADE_COMPARE(cb.backgroundColor, 0x00000000_rgbaf); + CORRADE_COMPARE(ca.materialId, 0); + CORRADE_COMPARE(cb.materialId, 0); CORRADE_VERIFY(std::is_nothrow_default_constructible::value); CORRADE_VERIFY(std::is_nothrow_constructible::value); @@ -94,16 +104,14 @@ void VectorTest::drawUniformConstructDefault() { void VectorTest::drawUniformConstructNoInit() { /* Testing only some fields, should be enough */ VectorDrawUniform a; - a.color = 0x354565fc_rgbaf; - a.backgroundColor = 0x98769facb_rgbaf; + a.materialId = 5; new(&a) VectorDrawUniform{NoInit}; { #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); #endif - CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); - CORRADE_COMPARE(a.backgroundColor, 0x98769facb_rgbaf); + CORRADE_COMPARE(a.materialId, 5); } CORRADE_VERIFY(std::is_nothrow_constructible::value); @@ -114,6 +122,63 @@ void VectorTest::drawUniformConstructNoInit() { void VectorTest::drawUniformSetters() { VectorDrawUniform a; + a.setMaterialId(5); + CORRADE_COMPARE(a.materialId, 5); +} + +void VectorTest::drawUniformMaterialIdPacking() { + VectorDrawUniform a; + a.setMaterialId(13765); + /* materialId should be right at the beginning, in the low 16 bits on both + LE and BE */ + CORRADE_COMPARE(reinterpret_cast(&a)[0] & 0xffff, 13765); +} + +void VectorTest::materialUniformConstructDefault() { + VectorMaterialUniform a; + VectorMaterialUniform b{DefaultInit}; + CORRADE_COMPARE(a.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(b.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(a.backgroundColor, 0x00000000_rgbaf); + CORRADE_COMPARE(b.backgroundColor, 0x00000000_rgbaf); + + constexpr VectorMaterialUniform ca; + constexpr VectorMaterialUniform cb{DefaultInit}; + CORRADE_COMPARE(ca.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(cb.color, 0xffffffff_rgbaf); + CORRADE_COMPARE(ca.backgroundColor, 0x00000000_rgbaf); + CORRADE_COMPARE(cb.backgroundColor, 0x00000000_rgbaf); + + CORRADE_VERIFY(std::is_nothrow_default_constructible::value); + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void VectorTest::materialUniformConstructNoInit() { + /* Testing only some fields, should be enough */ + VectorMaterialUniform a; + a.color = 0x354565fc_rgbaf; + a.backgroundColor = 0x98769facb_rgbaf; + + new(&a) VectorMaterialUniform{NoInit}; + { + #if defined(__GNUC__) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ + CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); + #endif + CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); + CORRADE_COMPARE(a.backgroundColor, 0x98769facb_rgbaf); + } + + CORRADE_VERIFY(std::is_nothrow_constructible::value); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!std::is_convertible::value); +} + +void VectorTest::materialUniformSetters() { + VectorMaterialUniform a; a.setColor(0x354565fc_rgbaf) .setBackgroundColor(0x98769facb_rgbaf); CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); diff --git a/src/Magnum/Shaders/Vector.frag b/src/Magnum/Shaders/Vector.frag index 8f1993944..a36e3a7a6 100644 --- a/src/Magnum/Shaders/Vector.frag +++ b/src/Magnum/Shaders/Vector.frag @@ -66,9 +66,8 @@ uniform highp uint drawOffset #endif struct DrawUniform { - lowp vec4 color; - lowp vec4 backgroundColor; - lowp uvec4 reserved; + highp uvec4 materialIdReservedReservedReservedReserved; + #define draw_materialIdReserved materialIdReservedReservedReservedReserved.x }; layout(std140 @@ -78,6 +77,20 @@ layout(std140 ) uniform Draw { DrawUniform draws[DRAW_COUNT]; }; + +struct MaterialUniform { + lowp vec4 color; + lowp vec4 backgroundColor; + lowp uvec4 reserved; +}; + +layout(std140 + #ifdef EXPLICIT_BINDING + , binding = 4 + #endif +) uniform Material { + MaterialUniform materials[MATERIAL_COUNT]; +}; #endif /* Textures */ @@ -106,8 +119,9 @@ out lowp vec4 fragmentColor; void main() { #ifdef UNIFORM_BUFFERS - lowp const vec4 color = draws[drawId].color; - lowp const vec4 backgroundColor = draws[drawId].backgroundColor; + mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; + lowp const vec4 color = materials[materialId].color; + lowp const vec4 backgroundColor = materials[materialId].backgroundColor; #endif lowp float intensity = texture(vectorTexture, interpolatedTextureCoordinates).r; diff --git a/src/Magnum/Shaders/Vector.h b/src/Magnum/Shaders/Vector.h index f45ce9df5..4d6b44675 100644 --- a/src/Magnum/Shaders/Vector.h +++ b/src/Magnum/Shaders/Vector.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Struct @ref Magnum::Shaders::VectorDrawUniform + * @brief Struct @ref Magnum::Shaders::VectorDrawUniform, @ref Magnum::Shaders::VectorMaterialUniform */ #include "Magnum/Magnum.h" @@ -47,20 +47,121 @@ namespace Magnum { namespace Shaders { Together with the generic @ref TransformationProjectionUniform2D / @ref TransformationProjectionUniform3D contains parameters that are specific to each draw call. Texture transformation, if needed, is supplied separately in a -@ref TextureTransformationUniform. +@ref TextureTransformationUniform; material-related properties are expected to +be shared among multiple draw calls and thus are provided in a separate +@ref VectorMaterialUniform structure, referenced by @ref materialId. @see @ref VectorGL::bindDrawBuffer() */ struct VectorDrawUniform { /** @brief Construct with default parameters */ - constexpr explicit VectorDrawUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f}, backgroundColor{0.0f, 0.0f, 0.0f, 0.0f} + constexpr explicit VectorDrawUniform(DefaultInitT = DefaultInit) noexcept: + #if ((defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8)) && defined(CORRADE_TARGET_BIG_ENDIAN) + _pad0{}, /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + materialId{0} + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) && !defined(CORRADE_TARGET_BIG_ENDIAN) + , _pad0{}, _pad1{}, _pad2{}, _pad3{} + #endif + {} + + /** @brief Construct without initializing the contents */ + explicit VectorDrawUniform(NoInitT) noexcept {} + + /** @{ + * @name Convenience setters + * + * Provided to allow the use of method chaining for populating a structure + * in a single expression, otherwise equivalent to accessing the fields + * directly. Also guaranteed to provide backwards compatibility when + * packing of the actual fields changes. + */ + + /** + * @brief Set the @ref materialId field + * @return Reference to self (for method chaining) + */ + VectorDrawUniform& setMaterialId(UnsignedInt id) { + materialId = id; + return *this; + } + + /** + * @} + */ + + /** @var materialId + * @brief Material ID + * + * References a particular material from a @ref VectorMaterialUniform + * array. Useful when an UBO with more than one material is supplied or in + * a multi-draw scenario. Should be less than the material count passed to + * the @ref VectorGL::VectorGL(Flags, UnsignedInt, UnsignedInt) + * constructor. Default value is @cpp 0 @ce, meaning the first material + * gets used. + */ + + /* This field is an UnsignedInt in the shader and materialId is extracted + as (value & 0xffff), so the order has to be different on BE */ + #ifndef CORRADE_TARGET_BIG_ENDIAN + alignas(4) UnsignedShort materialId; + /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; /* reserved for skinOffset */ + #endif + #else + alignas(4) UnsignedShort + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :16; /* reserved for skinOffset */ + UnsignedShort materialId; + #endif + + /* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK + I MADE THOSE UNNAMED, YOU DUMB FOOL */ + #ifndef DOXYGEN_GENERATING_OUTPUT + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; /* reserved for objectId */ + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad2 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; + Int + #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) + _pad3 /* Otherwise it refuses to constexpr, on 3.8 at least */ + #endif + :32; + #endif +}; + +/** +@brief Material uniform for vector shaders +@m_since_latest + +Describes material properties referenced from +@ref VectorDrawUniform::materialId. +@see @ref VectorGL::bindMaterialBuffer() +*/ +struct VectorMaterialUniform { + /** @brief Construct with default parameters */ + constexpr explicit VectorMaterialUniform(DefaultInitT = DefaultInit) noexcept: color{1.0f, 1.0f, 1.0f, 1.0f}, backgroundColor{0.0f, 0.0f, 0.0f, 0.0f} #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) /* Otherwise it refuses to constexpr, on 3.8 at least */ - , _pad0{}, _pad1{}, _pad2{}, _pad3{}, _pad4{} + , _pad0{}, _pad1{}, _pad2{}, _pad3{} #endif {} /** @brief Construct without initializing the contents */ - explicit VectorDrawUniform(NoInitT) noexcept: color{NoInit}, backgroundColor{NoInit} {} + explicit VectorMaterialUniform(NoInitT) noexcept: color{NoInit}, backgroundColor{NoInit} {} /** @{ * @name Convenience setters @@ -75,7 +176,7 @@ struct VectorDrawUniform { * @brief Set the @ref color field * @return Reference to self (for method chaining) */ - VectorDrawUniform& setColor(const Color4& color) { + VectorMaterialUniform& setColor(const Color4& color) { this->color = color; return *this; } @@ -84,7 +185,7 @@ struct VectorDrawUniform { * @brief Set the @ref backgroundColor field * @return Reference to self (for method chaining) */ - VectorDrawUniform& setBackgroundColor(const Color4& color) { + VectorMaterialUniform& setBackgroundColor(const Color4& color) { backgroundColor = color; return *this; } @@ -112,45 +213,25 @@ struct VectorDrawUniform { /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT - /* This field is an UnsignedInt in the shader and skinOffset is extracted - as (value >> 16), so the order has to be different on BE */ - #ifndef CORRADE_TARGET_BIG_ENDIAN - UnsignedShort - #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) - _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ - #endif - :16; - UnsignedShort - #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) - _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ - #endif - :16; /* reserved for skinOffset */ - #else - UnsignedShort + Int #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ #endif - :16; /* reserved for skinOffset */ - UnsignedShort + :32; /* reserved for alpha mask */ + Int #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ #endif - :16; - #endif + :32; Int #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) _pad2 /* Otherwise it refuses to constexpr, on 3.8 at least */ #endif - :32; /* reserved for objectId */ + :32; Int #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) _pad3 /* Otherwise it refuses to constexpr, on 3.8 at least */ #endif - :32; /* reserved for alphaMask */ - Int - #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) - _pad4 /* Otherwise it refuses to constexpr, on 3.8 at least */ - #endif :32; #endif }; diff --git a/src/Magnum/Shaders/VectorGL.cpp b/src/Magnum/Shaders/VectorGL.cpp index 9efddeb6b..bd98a6fdc 100644 --- a/src/Magnum/Shaders/VectorGL.cpp +++ b/src/Magnum/Shaders/VectorGL.cpp @@ -57,22 +57,25 @@ namespace { bound to the same buffer for the whole time */ TransformationProjectionBufferBinding = 1, DrawBufferBinding = 2, - TextureTransformationBufferBinding = 3 + TextureTransformationBufferBinding = 3, + MaterialBufferBinding = 4 }; #endif } template VectorGL::VectorGL(const Flags flags #ifndef MAGNUM_TARGET_GLES2 - , const UnsignedInt drawCount + , const UnsignedInt materialCount, const UnsignedInt drawCount #endif ): _flags{flags} #ifndef MAGNUM_TARGET_GLES2 - , _drawCount{drawCount} + , _materialCount{materialCount}, _drawCount{drawCount} #endif { #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || materialCount, + "Shaders::VectorGL: material count can't be zero", ); CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || drawCount, "Shaders::VectorGL: draw count can't be zero", ); #endif @@ -128,8 +131,10 @@ template VectorGL::VectorGL(const Flags flag if(flags >= Flag::UniformBuffers) { frag.addSource(Utility::formatString( "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n", - drawCount)); + "#define DRAW_COUNT {}\n" + "#define MATERIAL_COUNT {}\n", + drawCount, + materialCount)); frag.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif @@ -182,6 +187,7 @@ template VectorGL::VectorGL(const Flags flag setUniformBlockBinding(uniformBlockIndex("Draw"), DrawBufferBinding); if(flags & Flag::TextureTransformation) setUniformBlockBinding(uniformBlockIndex("TextureTransformation"), TextureTransformationBufferBinding); + setUniformBlockBinding(uniformBlockIndex("Material"), MaterialBufferBinding); } #endif } @@ -204,7 +210,7 @@ template VectorGL::VectorGL(const Flags flag } #ifndef MAGNUM_TARGET_GLES2 -template VectorGL::VectorGL(const Flags flags): VectorGL{flags, 1} {} +template VectorGL::VectorGL(const Flags flags): VectorGL{flags, 1, 1} {} #endif template VectorGL& VectorGL::setTransformationProjectionMatrix(const MatrixTypeFor& matrix) { @@ -300,6 +306,20 @@ template VectorGL& VectorGL::bin buffer.bind(GL::Buffer::Target::Uniform, TextureTransformationBufferBinding, offset, size); return *this; } + +template VectorGL& VectorGL::bindMaterialBuffer(GL::Buffer& buffer) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::VectorGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding); + return *this; +} + +template VectorGL& VectorGL::bindMaterialBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { + CORRADE_ASSERT(_flags >= Flag::UniformBuffers, + "Shaders::VectorGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); + buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); + return *this; +} #endif template VectorGL& VectorGL::bindVectorTexture(GL::Texture2D& texture) { diff --git a/src/Magnum/Shaders/VectorGL.h b/src/Magnum/Shaders/VectorGL.h index d5fd88d10..51068ad54 100644 --- a/src/Magnum/Shaders/VectorGL.h +++ b/src/Magnum/Shaders/VectorGL.h @@ -127,8 +127,8 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL /** * Use uniform buffers. Expects that uniform data are supplied via * @ref bindTransformationProjectionBuffer(), - * @ref bindDrawBuffer() and @ref bindTextureTransformationBuffer() - * instead of direct uniform setters. + * @ref bindDrawBuffer(), @ref bindTextureTransformationBuffer() + * and @ref bindMaterialBuffer() instead of direct uniform setters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES * 2.0. @@ -185,7 +185,8 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * * While this function is meant mainly for the classic uniform * scenario (without @ref Flag::UniformBuffers set), it's equivalent to - * @ref VectorGL(Flags, UnsignedInt) with @p drawCount set to @cpp 1 @ce. + * @ref VectorGL(Flags, UnsignedInt, UnsignedInt) with @p materialCount + * and @p drawCount set to @cpp 1 @ce. */ explicit VectorGL(Flags flags = {}); @@ -193,16 +194,19 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL /** * @brief Construct for a multi-draw scenario * @param flags Flags + * @param materialCount Size of a @ref VectorMaterialUniform buffer + * bound with @ref bindMaterialBuffer() * @param drawCount Size of a @ref TransformationProjectionUniform2D * / @ref TransformationProjectionUniform3D / * @ref VectorDrawUniform / @ref TextureTransformationUniform * buffer bound with @ref bindTransformationProjectionBuffer(), * @ref bindDrawBuffer() and @ref bindTextureTransformationBuffer() * - * If @p flags contains @ref Flag::UniformBuffers @p drawCount - * describes the uniform buffer sizes as these are required to have a - * statically defined size. The draw offset is then set via - * @ref setDrawOffset(). + * If @p flags contains @ref Flag::UniformBuffers, @p materialCount and + * @p drawCount describe the uniform buffer sizes as these are required + * to have a statically defined size. The draw offset is then set via + * @ref setDrawOffset() and the per-draw materials specified via + * @ref VectorDrawUniform::materialId. * * If @p flags don't contain @ref Flag::UniformBuffers, @p drawCount is * ignored and the constructor behaves the same as @ref VectorGL(Flags). @@ -217,7 +221,7 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL (accepting pairs of parameter type and value like in GL context creation, e.g., which will probably need a new enum as reusing Flag for this might be too confusing) */ - explicit VectorGL(Flags flags, UnsignedInt drawCount); + explicit VectorGL(Flags flags, UnsignedInt materialCount, UnsignedInt drawCount); #endif /** @@ -253,6 +257,18 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL Flags flags() const { return _flags; } #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Material count + * @m_since_latest + * + * Statically defined size of the @ref VectorMaterialUniform uniform + * buffer. Has use only if @ref Flag::UniformBuffers is set. + * @see @ref bindMaterialBuffer() + * @requires_gles30 Not defined on OpenGL ES 2.0 builds. + * @requires_webgl20 Not defined on WebGL 1.0 builds. + */ + UnsignedInt materialCount() const { return _materialCount; } + /** * @brief Draw count * @m_since_latest @@ -310,8 +326,8 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * Initial value is @cpp 0x00000000_rgbaf @ce. * * Expects that @ref Flag::UniformBuffers is not set, in that case fill - * @ref VectorDrawUniform::backgroundColor and call - * @ref bindDrawBuffer() instead. + * @ref VectorMaterialUniform::backgroundColor and call + * @ref bindMaterialBuffer() instead. * @see @ref setColor() */ VectorGL& setBackgroundColor(const Color4& color); @@ -323,7 +339,7 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * Initial value is @cpp 0xffffffff_rgbaf @ce. * * Expects that @ref Flag::UniformBuffers is not set, in that case fill - * @ref VectorDrawUniform::color and call @ref bindDrawBuffer() + * @ref VectorMaterialUniform::color and call @ref bindMaterialBuffer() * instead. * @see @ref setBackgroundColor() */ @@ -373,7 +389,7 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * expected to contain @ref drawCount() instances of * @ref TransformationProjectionUniform2D / * @ref TransformationProjectionUniform3D. At the very least you need - * to call also @ref bindDrawBuffer(). + * to call also @ref bindDrawBuffer() and @ref bindMaterialBuffer(). * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. @@ -393,7 +409,8 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * Expects that @ref Flag::UniformBuffers is set. The buffer is * expected to contain @ref drawCount() instances of * @ref VectorDrawUniform. At the very least you need to call also - * @ref bindTransformationProjectionBuffer(). + * @ref bindTransformationProjectionBuffer() and + * @ref bindMaterialBuffer(). * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. @@ -425,6 +442,26 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL */ VectorGL& bindTextureTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + /** + * @brief Set a material uniform buffer + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that @ref Flag::UniformBuffers is set. The buffer is + * expected to contain @ref materialCount() instances of + * @ref VectorMaterialUniform. At the very least you need to call also + * @ref bindTransformationProjectionBuffer() and @ref bindDrawBuffer(). + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + */ + VectorGL& bindMaterialBuffer(GL::Buffer& buffer); + /** + * @overload + * @m_since_latest + */ + VectorGL& bindMaterialBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); + /** * @} */ @@ -457,7 +494,7 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL Flags _flags; #ifndef MAGNUM_TARGET_GLES2 - UnsignedInt _drawCount{}; + UnsignedInt _materialCount{}, _drawCount{}; #endif Int _transformationProjectionMatrixUniform{0}, _textureMatrixUniform{1}, From 1ce1561eb269add9aabbb9353492b910ef100507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 31 May 2021 16:54:02 +0200 Subject: [PATCH 40/93] Shaders: expand Flat UBO construction tests a bit. --- src/Magnum/Shaders/Test/FlatGLTest.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index b2581c749..f962d0913 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -217,7 +217,8 @@ constexpr struct { /* SwiftShader has 256 uniform vectors at most, per-draw is 4+1 in 3D case and 3+1 in 2D, per-material 2 */ {"multiple materials, draws", FlatGL2D::Flag::UniformBuffers, 8, 48}, - {"texture transformation", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1, 1}, + {"textured", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured, 1, 1}, + {"textured + 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} From 4c4f63b5a0c5ff7f7bd8e7ec21013bd60d5881d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 31 May 2021 16:57:53 +0200 Subject: [PATCH 41/93] Shaders: test instanced object ID in the instanced test case. Is more self-contained that way (the new multidraw tests were done this way also). --- src/Magnum/Shaders/Test/FlatGLTest.cpp | 196 ++++++++++++++++-------- src/Magnum/Shaders/Test/PhongGLTest.cpp | 119 ++++++++------ 2 files changed, 203 insertions(+), 112 deletions(-) diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index f962d0913..5b68c7978 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -274,23 +274,6 @@ const struct { FlatGL2D::Flag::Textured|FlatGL2D::Flag::AlphaMask, 1.0f} }; -#ifndef MAGNUM_TARGET_GLES2 -constexpr struct { - const char* name; - FlatGL2D::Flags flags; - UnsignedInt uniformId; - UnsignedInt instanceCount; - UnsignedInt expected; -} RenderObjectIdData[] { - {"", /* Verify that it can hold 16 bits at least */ - FlatGL2D::Flag::ObjectId, 48526, 0, 48526}, - {"instanced, first instance", - FlatGL2D::Flag::InstancedObjectId, 13524, 1, 24526}, - {"instanced, second instance", - FlatGL2D::Flag::InstancedObjectId, 13524, 2, 62347} -}; -#endif - #ifndef MAGNUM_TARGET_GLES2 constexpr struct { const char* name; @@ -469,12 +452,11 @@ FlatGLTest::FlatGLTest() { #ifndef MAGNUM_TARGET_GLES2 /* MSVC needs explicit type due to default template args */ - addInstancedTests({ + addTests({ &FlatGLTest::renderObjectId2D, &FlatGLTest::renderObjectId2D, &FlatGLTest::renderObjectId3D, &FlatGLTest::renderObjectId3D}, - Containers::arraySize(RenderObjectIdData), &FlatGLTest::renderObjectIdSetup, &FlatGLTest::renderObjectIdTeardown); #endif @@ -490,8 +472,14 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderInstanced3D #endif }, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::renderObjectIdSetup, + &FlatGLTest::renderObjectIdTeardown + #else &FlatGLTest::renderSetup, - &FlatGLTest::renderTeardown); + &FlatGLTest::renderTeardown + #endif + ); #ifndef MAGNUM_TARGET_GLES2 addInstancedTests({&FlatGLTest::renderMulti2D, @@ -1929,9 +1917,6 @@ void FlatGLTest::renderObjectIdTeardown() { } template void FlatGLTest::renderObjectId2D() { - auto&& data = RenderObjectIdData[testCaseInstanceId()]; - setTestCaseDescription(data.name); - #ifndef MAGNUM_TARGET_GLES2 if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -1952,18 +1937,12 @@ template void FlatGLTest::renderObjectId2D() { GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32)); - if(data.instanceCount) circle - .setInstanceCount(data.instanceCount) - .addVertexBufferInstanced( - GL::Buffer{Containers::arrayView({11002u, 48823u})}, - 1, 0, FlatGL2D::ObjectId{}); - - FlatGL2D shader{data.flags|flag}; + FlatGL2D shader{FlatGL2D::Flag::ObjectId|flag}; if(flag == FlatGL2D::Flag{}) { shader.setColor(0x9999ff_rgbf) .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) - .setObjectId(data.uniformId) + .setObjectId(48526) .draw(circle); } else if(flag == FlatGL2D::Flag::UniformBuffers) { GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { @@ -1972,7 +1951,7 @@ template void FlatGLTest::renderObjectId2D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} - .setObjectId(data.uniformId) + .setObjectId(48526) }}; GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { FlatMaterialUniform{} @@ -2014,13 +1993,10 @@ template void FlatGLTest::renderObjectId2D() { /* Outside of the object, cleared to 27 */ CORRADE_COMPARE(image.pixels()[10][10], 27); /* Inside of the object */ - CORRADE_COMPARE(image.pixels()[40][46], data.expected); + CORRADE_COMPARE(image.pixels()[40][46], 48526); } template void FlatGLTest::renderObjectId3D() { - auto&& data = RenderObjectIdData[testCaseInstanceId()]; - setTestCaseDescription(data.name); - #ifndef MAGNUM_TARGET_GLES2 if(flag == FlatGL3D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2041,13 +2017,7 @@ template void FlatGLTest::renderObjectId3D() { GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); - if(data.instanceCount) sphere - .setInstanceCount(data.instanceCount) - .addVertexBufferInstanced( - GL::Buffer{Containers::arrayView({11002u, 48823u})}, - 1, 0, FlatGL2D::ObjectId{}); - - FlatGL3D shader{data.flags|flag}; + FlatGL3D shader{FlatGL3D::Flag::ObjectId|flag}; if(flag == FlatGL3D::Flag{}) { shader.setColor(0x9999ff_rgbf) @@ -2056,7 +2026,7 @@ template void FlatGLTest::renderObjectId3D() { Matrix4::translation(Vector3::zAxis(-2.15f))* Matrix4::rotationY(-15.0_degf)* Matrix4::rotationX(15.0_degf)) - .setObjectId(data.uniformId) + .setObjectId(48526) .draw(sphere); } else if(flag == FlatGL3D::Flag::UniformBuffers) { GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, { @@ -2070,7 +2040,7 @@ template void FlatGLTest::renderObjectId3D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} - .setObjectId(data.uniformId) + .setObjectId(48526) }}; GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { FlatMaterialUniform{} @@ -2115,7 +2085,7 @@ template void FlatGLTest::renderObjectId3D() { /* Outside of the object, cleared to 27 */ CORRADE_COMPARE(image.pixels()[10][10], 27); /* Inside of the object */ - CORRADE_COMPARE(image.pixels()[40][46], data.expected); + CORRADE_COMPARE(image.pixels()[40][46], 48526); } #endif @@ -2158,20 +2128,27 @@ template void FlatGLTest::renderInstanced2D() { Matrix3 transformation; Color3 color; Vector2 textureOffset; + UnsignedInt objectId; } instanceData[] { {Matrix3::translation({-1.25f, -1.25f}), 0xff3333_rgbf, - {0.0f, 0.0f}}, + {0.0f, 0.0f}, 211}, {Matrix3::translation({ 1.25f, -1.25f}), 0x33ff33_rgbf, - {1.0f, 0.0f}}, + {1.0f, 0.0f}, 4627}, {Matrix3::translation({ 0.00f, 1.25f}), 0x9999ff_rgbf, - {0.5f, 1.0f}} + {0.5f, 1.0f}, 35363}, }; circle .addVertexBufferInstanced(GL::Buffer{instanceData}, 1, 0, FlatGL2D::TransformationMatrix{}, FlatGL2D::Color3{}, - FlatGL2D::TextureOffset{}) + FlatGL2D::TextureOffset{}, + #ifndef MAGNUM_TARGET_GLES2 + FlatGL2D::ObjectId{} + #else + 4 + #endif + ) .setInstanceCount(3); Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); @@ -2186,10 +2163,19 @@ template void FlatGLTest::renderInstanced2D() { .setStorage(1, TextureFormatRGB, image->size()) .setSubImage(0, {}, *image); - FlatGL2D shader{FlatGL2D::Flag::Textured| - FlatGL2D::Flag::VertexColor| - FlatGL2D::Flag::InstancedTransformation| - FlatGL2D::Flag::InstancedTextureOffset|flag}; + /* Enable also Object ID, if supported */ + FlatGL2D::Flags flags = FlatGL2D::Flag::Textured| + FlatGL2D::Flag::VertexColor|FlatGL2D::Flag::InstancedTransformation| + FlatGL2D::Flag::InstancedTextureOffset|flag; + #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + #endif + { + flags |= FlatGL2D::Flag::InstancedObjectId; + } + #endif + FlatGL2D shader{flags}; shader.bindTexture(texture); if(flag == FlatGL2D::Flag{}) { @@ -2197,8 +2183,18 @@ template void FlatGLTest::renderInstanced2D() { .setTransformationProjectionMatrix( Matrix3::projection({2.1f, 2.1f})* Matrix3::scaling(Vector2{0.4f})) - .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) - .draw(circle); + .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})); + + #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + #endif + { + shader.setObjectId(1000); /* gets added to the per-instance ID */ + } + #endif + + shader.draw(circle); } #ifndef MAGNUM_TARGET_GLES2 else if(flag == FlatGL2D::Flag::UniformBuffers) { @@ -2211,6 +2207,7 @@ template void FlatGLTest::renderInstanced2D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} + .setObjectId(1000) /* gets added to the per-instance ID */ }}; GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} @@ -2243,6 +2240,26 @@ template void FlatGLTest::renderInstanced2D() { Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), Utility::Directory::join(_testDir, "FlatTestFiles/instanced2D.tga"), (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); + + #ifndef MAGNUM_TARGET_GLES2 + /* Object ID -- no need to verify the whole image, just check that pixels + on known places have expected values. SwiftShader insists that the read + format has to be 32bit, so the renderbuffer format is that too to make + it the same (ES3 Mesa complains if these don't match). */ + #ifndef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + #endif + { + _framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1}); + CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete); + Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI}); + MAGNUM_VERIFY_NO_GL_ERROR(); + CORRADE_COMPARE(image.pixels()[5][5], 27); /* Outside */ + CORRADE_COMPARE(image.pixels()[24][24], 1211); + CORRADE_COMPARE(image.pixels()[24][56], 5627); + CORRADE_COMPARE(image.pixels()[56][40], 36363); + } + #endif } template void FlatGLTest::renderInstanced3D() { @@ -2284,20 +2301,27 @@ template void FlatGLTest::renderInstanced3D() { Matrix4 transformation; Color3 color; Vector2 textureOffset; + UnsignedInt objectId; } instanceData[] { {Matrix4::translation({-1.25f, -1.25f, 0.0f}), 0xff3333_rgbf, - {0.0f, 0.0f}}, + {0.0f, 0.0f}, 211}, {Matrix4::translation({ 1.25f, -1.25f, 0.0f}), 0x33ff33_rgbf, - {1.0f, 0.0f}}, + {1.0f, 0.0f}, 4627}, {Matrix4::translation({ 0.0f, 1.0f, 1.0f}), 0x9999ff_rgbf, - {0.5f, 1.0f}} + {0.5f, 1.0f}, 35363} }; sphere .addVertexBufferInstanced(GL::Buffer{instanceData}, 1, 0, FlatGL3D::TransformationMatrix{}, FlatGL3D::Color3{}, - FlatGL3D::TextureOffset{}) + FlatGL3D::TextureOffset{}, + #ifndef MAGNUM_TARGET_GLES2 + FlatGL2D::ObjectId{} + #else + 4 + #endif + ) .setInstanceCount(3); Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); @@ -2312,10 +2336,19 @@ template void FlatGLTest::renderInstanced3D() { .setStorage(1, TextureFormatRGB, image->size()) .setSubImage(0, {}, *image); - FlatGL3D shader{FlatGL3D::Flag::Textured| - FlatGL3D::Flag::VertexColor| - FlatGL3D::Flag::InstancedTransformation| - FlatGL3D::Flag::InstancedTextureOffset|flag}; + /* Enable also Object ID, if supported */ + FlatGL3D::Flags flags = FlatGL3D::Flag::Textured| + FlatGL3D::Flag::VertexColor|FlatGL3D::Flag::InstancedTransformation| + FlatGL3D::Flag::InstancedTextureOffset|flag; + #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + #endif + { + flags |= FlatGL2D::Flag::InstancedObjectId; + } + #endif + FlatGL3D shader{flags}; shader.bindTexture(texture); if(flag == FlatGL3D::Flag{}) { @@ -2324,8 +2357,18 @@ template void FlatGLTest::renderInstanced3D() { 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})) - .draw(sphere); + .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})); + + #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + #endif + { + shader.setObjectId(1000); /* gets added to the per-instance ID */ + } + #endif + + shader.draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 else if(flag == FlatGL2D::Flag::UniformBuffers) { @@ -2339,6 +2382,7 @@ template void FlatGLTest::renderInstanced3D() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { FlatDrawUniform{} + .setObjectId(1000) /* gets added to the per-instance ID */ }}; GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} @@ -2371,6 +2415,26 @@ template void FlatGLTest::renderInstanced3D() { Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), Utility::Directory::join(_testDir, "FlatTestFiles/instanced3D.tga"), (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); + + #ifndef MAGNUM_TARGET_GLES2 + /* Object ID -- no need to verify the whole image, just check that pixels + on known places have expected values. SwiftShader insists that the read + format has to be 32bit, so the renderbuffer format is that too to make + it the same (ES3 Mesa complains if these don't match). */ + #ifndef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + #endif + { + _framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1}); + CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete); + Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI}); + MAGNUM_VERIFY_NO_GL_ERROR(); + CORRADE_COMPARE(image.pixels()[5][5], 27); /* Outside */ + CORRADE_COMPARE(image.pixels()[24][24], 1211); + CORRADE_COMPARE(image.pixels()[24][56], 5627); + CORRADE_COMPARE(image.pixels()[56][40], 36363); + } + #endif } #ifndef MAGNUM_TARGET_GLES2 diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index ce99031f0..100e2a576 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -433,23 +433,6 @@ const struct { 0xffffffff_rgbaf, 0x9999ff00_rgbaf} }; -#ifndef MAGNUM_TARGET_GLES2 -constexpr struct { - const char* name; - PhongGL::Flags flags; - UnsignedInt uniformId; - UnsignedInt instanceCount; - UnsignedInt expected; -} RenderObjectIdData[] { - {"", /* Verify that it can hold 16 bits at least */ - PhongGL::Flag::ObjectId, 48526, 0, 48526}, - {"instanced, first instance", - PhongGL::Flag::InstancedObjectId, 13524, 1, 24526}, - {"instanced, second instance", - PhongGL::Flag::InstancedObjectId, 13524, 2, 62347} -}; -#endif - const struct { const char* name; const char* file; @@ -772,10 +755,9 @@ PhongGLTest::PhongGLTest() { #ifndef MAGNUM_TARGET_GLES2 /* MSVC needs explicit type due to default template args */ - addInstancedTests({ + addTests({ &PhongGLTest::renderObjectId, &PhongGLTest::renderObjectId}, - Containers::arraySize(RenderObjectIdData), &PhongGLTest::renderObjectIdSetup, &PhongGLTest::renderObjectIdTeardown); #endif @@ -820,8 +802,14 @@ PhongGLTest::PhongGLTest() { #endif }, Containers::arraySize(RenderInstancedData), + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::renderObjectIdSetup, + &PhongGLTest::renderObjectIdTeardown + #else &PhongGLTest::renderSetup, - &PhongGLTest::renderTeardown); + &PhongGLTest::renderTeardown + #endif + ); #ifndef MAGNUM_TARGET_GLES2 addInstancedTests({&PhongGLTest::renderMulti}, @@ -2349,9 +2337,6 @@ void PhongGLTest::renderObjectIdTeardown() { } template void PhongGLTest::renderObjectId() { - auto&& data = RenderObjectIdData[testCaseInstanceId()]; - setTestCaseDescription(data.name); - #ifndef MAGNUM_TARGET_GLES2 if(flag == PhongGL::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2372,13 +2357,7 @@ template void PhongGLTest::renderObjectId() { GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); - if(data.instanceCount) sphere - .setInstanceCount(data.instanceCount) - .addVertexBufferInstanced( - GL::Buffer{Containers::arrayView({11002u, 48823u})}, - 1, 0, PhongGL::ObjectId{}); - - PhongGL shader{data.flags|flag, 2}; + PhongGL shader{PhongGL::Flag::ObjectId|flag, 2}; if(flag == PhongGL::Flag{}) { shader @@ -2390,7 +2369,7 @@ template void PhongGLTest::renderObjectId() { .setSpecularColor(0x6666ff_rgbf) .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f))) .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) - .setObjectId(data.uniformId) + .setObjectId(48526) .draw(sphere); } else if(flag == PhongGL::Flag::UniformBuffers) { GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { @@ -2404,7 +2383,8 @@ template void PhongGLTest::renderObjectId() { ) }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { - PhongDrawUniform{}.setObjectId(data.uniformId) + PhongDrawUniform{} + .setObjectId(48526) }}; GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, { PhongLightUniform{} @@ -2460,7 +2440,7 @@ template void PhongGLTest::renderObjectId() { /* Outside of the object, cleared to 27 */ CORRADE_COMPARE(image.pixels()[10][10], 27); /* Inside of the object */ - CORRADE_COMPARE(image.pixels()[40][46], data.expected); + CORRADE_COMPARE(image.pixels()[40][46], 48526); } #endif @@ -2868,17 +2848,17 @@ template void PhongGLTest::renderInstanced() { Matrix3x3 normal; Color3 color; Vector2 textureOffset; - /* instanced ObjectId tested directly in renderObjectId() */ + UnsignedInt objectId; } instanceData[] { {Matrix4::translation({-1.25f, -1.25f, 0.0f})* Matrix4::rotationX(90.0_degf), - {}, 0xff3333_rgbf, {0.0f, 0.0f}}, + {}, 0xff3333_rgbf, {0.0f, 0.0f}, 211}, {Matrix4::translation({ 1.25f, -1.25f, 0.0f})* Matrix4::rotationY(90.0_degf), - {}, 0x33ff33_rgbf, {1.0f, 0.0f}}, + {}, 0x33ff33_rgbf, {1.0f, 0.0f}, 4627}, {Matrix4::translation({ 0.0f, 1.0f, 1.0f})* Matrix4::rotationZ(90.0_degf), - {}, 0x9999ff_rgbf, {0.5f, 1.0f}} + {}, 0x9999ff_rgbf, {0.5f, 1.0f}, 35363} }; for(auto& instance: instanceData) instance.normal = instance.transformation.normalMatrix(); @@ -2888,7 +2868,13 @@ template void PhongGLTest::renderInstanced() { PhongGL::TransformationMatrix{}, PhongGL::NormalMatrix{}, PhongGL::Color3{}, - PhongGL::TextureOffset{}) + PhongGL::TextureOffset{}, + #ifndef MAGNUM_TARGET_GLES2 + PhongGL::ObjectId{} + #else + 4 + #endif + ) .setInstanceCount(3); Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); @@ -2911,10 +2897,20 @@ template void PhongGLTest::renderInstanced() { .setStorage(1, TextureFormatRGB, image->size()) .setSubImage(0, {}, *image); - PhongGL shader{PhongGL::Flag::DiffuseTexture| + /* Enable also Object ID, if supported */ + PhongGL::Flags flags = PhongGL::Flag::DiffuseTexture| PhongGL::Flag::VertexColor| PhongGL::Flag::InstancedTransformation| - PhongGL::Flag::InstancedTextureOffset|data.flags|flag, 2}; + PhongGL::Flag::InstancedTextureOffset|data.flags|flag; + #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + #endif + { + flags |= PhongGL::Flag::InstancedObjectId; + } + #endif + PhongGL shader{flags, 2}; shader.bindDiffuseTexture(diffuse); if(data.flags & PhongGL::Flag::NormalTexture) shader.bindNormalTexture(normal); @@ -2933,8 +2929,18 @@ template void PhongGLTest::renderInstanced() { .setProjectionMatrix( Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) - .setDiffuseColor(0xffff99_rgbf) - .draw(sphere); + .setDiffuseColor(0xffff99_rgbf); + + #ifndef MAGNUM_TARGET_GLES2 + #ifndef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + #endif + { + shader.setObjectId(1000); /* gets added to the per-instance ID */ + } + #endif + + shader.draw(sphere); } #ifndef MAGNUM_TARGET_GLES2 else if(flag == PhongGL::Flag::UniformBuffers) { @@ -2952,10 +2958,11 @@ template void PhongGLTest::renderInstanced() { ) }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { - PhongDrawUniform{}.setNormalMatrix( - (Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)).normalMatrix() - ) + PhongDrawUniform{} + .setNormalMatrix( + (Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)).normalMatrix()) + .setObjectId(1000) /* gets added to the per-instance ID */ }}; GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { PhongMaterialUniform{} @@ -2986,6 +2993,26 @@ template void PhongGLTest::renderInstanced() { Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), Utility::Directory::join({_testDir, "PhongTestFiles", data.file}), (DebugTools::CompareImageToFile{_manager, data.maxThreshold, data.meanThreshold})); + + #ifndef MAGNUM_TARGET_GLES2 + /* Object ID -- no need to verify the whole image, just check that pixels + on known places have expected values. SwiftShader insists that the read + format has to be 32bit, so the renderbuffer format is that too to make + it the same (ES3 Mesa complains if these don't match). */ + #ifndef MAGNUM_TARGET_GLES + if(GL::Context::current().isExtensionSupported()) + #endif + { + _framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1}); + CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete); + Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI}); + MAGNUM_VERIFY_NO_GL_ERROR(); + CORRADE_COMPARE(image.pixels()[5][5], 27); /* Outside */ + CORRADE_COMPARE(image.pixels()[24][24], 1211); + CORRADE_COMPARE(image.pixels()[24][56], 5627); + CORRADE_COMPARE(image.pixels()[56][40], 36363); + } + #endif } #ifndef MAGNUM_TARGET_GLES2 From 45f115b931e530f15ee72ef0227c3472ebf06bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 31 May 2021 18:10:08 +0200 Subject: [PATCH 42/93] Shaders: separate instanced colored and textured test cases. With everything smashed together it was quite annoying to eyeball and too easy to miss issues. --- src/Magnum/Shaders/Test/CMakeLists.txt | 2 + src/Magnum/Shaders/Test/FlatGLTest.cpp | 236 +++++++++++------- .../FlatTestFiles/instanced-textured2D.tga | Bin 0 -> 2936 bytes .../FlatTestFiles/instanced-textured3D.tga | Bin 0 -> 2609 bytes .../Test/FlatTestFiles/instanced2D.tga | Bin 2630 -> 1058 bytes .../Test/FlatTestFiles/instanced3D.tga | Bin 2086 -> 1034 bytes src/Magnum/Shaders/Test/PhongGLTest.cpp | 204 ++++++++------- .../PhongTestFiles/instanced-textured.tga | Bin 0 -> 6512 bytes .../Shaders/Test/PhongTestFiles/instanced.tga | Bin 9748 -> 6448 bytes 9 files changed, 270 insertions(+), 172 deletions(-) create mode 100644 src/Magnum/Shaders/Test/FlatTestFiles/instanced-textured2D.tga create mode 100644 src/Magnum/Shaders/Test/FlatTestFiles/instanced-textured3D.tga create mode 100644 src/Magnum/Shaders/Test/PhongTestFiles/instanced-textured.tga diff --git a/src/Magnum/Shaders/Test/CMakeLists.txt b/src/Magnum/Shaders/Test/CMakeLists.txt index da3ceb234..277a3da9f 100644 --- a/src/Magnum/Shaders/Test/CMakeLists.txt +++ b/src/Magnum/Shaders/Test/CMakeLists.txt @@ -152,6 +152,8 @@ if(BUILD_GL_TESTS) FlatTestFiles/defaults.tga FlatTestFiles/instanced2D.tga FlatTestFiles/instanced3D.tga + FlatTestFiles/instanced-textured2D.tga + FlatTestFiles/instanced-textured3D.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 5b68c7978..5a50d4e87 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -166,21 +166,21 @@ struct FlatGLTest: GL::OpenGLTester { Mesa Intel BADIOM ES2 xx - ES3 BADIOx - Mesa AMD BADI - Mesa llvmpipe BADI - SwiftShader ES2 BADIxx - ES3 BADI + ES3 BAD Ox + Mesa AMD BAD + Mesa llvmpipe BAD + SwiftShader ES2 BAD xx + ES3 BAD ANGLE ES2 xx - ES3 BADIOM + ES3 BAD OM ARM Mali (Huawei P10) ES2 BAD xx - ES3 BADIOx + ES3 BAD Ox WebGL (on Mesa Intel) 1.0 BAD xx - 2.0 BADIOM + 2.0 BAD OM NVidia BAD Intel Windows BAD AMD macOS BAD - Intel macOS BADIOx + Intel macOS BAD Ox iPhone 6 w/ iOS 12.4 ES3 BAD x */ @@ -274,6 +274,23 @@ const struct { FlatGL2D::Flag::Textured|FlatGL2D::Flag::AlphaMask, 1.0f} }; +constexpr struct { + const char* name; + const char* expected2D; + const char* expected3D; + FlatGL2D::Flags flags; + Float maxThreshold, meanThreshold; +} RenderInstancedData[] { + {"colored", "instanced2D.tga", "instanced3D.tga", + {}, + /* Minor differences on SwiftShader */ + 164.4f, 0.094f}, + {"textured", "instanced-textured2D.tga", "instanced-textured3D.tga", + FlatGL2D::Flag::InstancedTextureOffset|FlatGL2D::Flag::Textured, + /* Minor differences on SwiftShader */ + 192.67f, 0.140f}, +}; + #ifndef MAGNUM_TARGET_GLES2 constexpr struct { const char* name; @@ -462,7 +479,7 @@ FlatGLTest::FlatGLTest() { #endif /* MSVC needs explicit type due to default template args */ - addTests({ + addInstancedTests({ &FlatGLTest::renderInstanced2D, #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderInstanced2D, @@ -472,6 +489,7 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderInstanced3D #endif }, + Containers::arraySize(RenderInstancedData), #ifndef MAGNUM_TARGET_GLES2 &FlatGLTest::renderObjectIdSetup, &FlatGLTest::renderObjectIdTeardown @@ -2090,6 +2108,9 @@ template void FlatGLTest::renderObjectId3D() { #endif template void FlatGLTest::renderInstanced2D() { + auto&& data = RenderInstancedData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2116,10 +2137,6 @@ template void FlatGLTest::renderInstanced2D() { #endif #endif - if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || - !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) - CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); - GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32, Primitives::Circle2DFlag::TextureCoordinates)); @@ -2130,11 +2147,14 @@ template void FlatGLTest::renderInstanced2D() { Vector2 textureOffset; UnsignedInt objectId; } instanceData[] { - {Matrix3::translation({-1.25f, -1.25f}), 0xff3333_rgbf, + {Matrix3::translation({-1.25f, -1.25f}), + data.flags & FlatGL2D::Flag::Textured ? 0xffffff_rgbf : 0xffff00_rgbf, {0.0f, 0.0f}, 211}, - {Matrix3::translation({ 1.25f, -1.25f}), 0x33ff33_rgbf, + {Matrix3::translation({ 1.25f, -1.25f}), + data.flags & FlatGL2D::Flag::Textured ? 0xffffff_rgbf : 0x00ffff_rgbf, {1.0f, 0.0f}, 4627}, - {Matrix3::translation({ 0.00f, 1.25f}), 0x9999ff_rgbf, + {Matrix3::translation({ 0.00f, 1.25f}), + data.flags & FlatGL2D::Flag::Textured ? 0xffffff_rgbf : 0xff00ff_rgbf, {0.5f, 1.0f}, 35363}, }; @@ -2151,22 +2171,9 @@ template void FlatGLTest::renderInstanced2D() { ) .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); - /* Enable also Object ID, if supported */ - FlatGL2D::Flags flags = FlatGL2D::Flag::Textured| - FlatGL2D::Flag::VertexColor|FlatGL2D::Flag::InstancedTransformation| - FlatGL2D::Flag::InstancedTextureOffset|flag; + FlatGL2D::Flags flags = FlatGL2D::Flag::VertexColor| + FlatGL2D::Flag::InstancedTransformation|data.flags|flag; #ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES if(GL::Context::current().isExtensionSupported()) @@ -2176,14 +2183,36 @@ template void FlatGLTest::renderInstanced2D() { } #endif FlatGL2D shader{flags}; - shader.bindTexture(texture); + + GL::Texture2D texture; + 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."); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + 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); + } if(flag == FlatGL2D::Flag{}) { - shader.setColor(0xffff99_rgbf) + shader + .setColor(data.flags & FlatGL2D::Flag::Textured ? 0xffffff_rgbf : 0xffff00_rgbf) .setTransformationProjectionMatrix( Matrix3::projection({2.1f, 2.1f})* - Matrix3::scaling(Vector2{0.4f})) - .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})); + Matrix3::scaling(Vector2{0.4f})); + + if(data.flags & FlatGL3D::Flag::Textured) + shader.setTextureMatrix(Matrix3::scaling(Vector2{0.5f})); #ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES @@ -2215,11 +2244,12 @@ template void FlatGLTest::renderInstanced2D() { }}; GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { FlatMaterialUniform{} - .setColor(0xffff99_rgbf) + .setColor(data.flags & FlatGL2D::Flag::Textured ? 0xffffff_rgbf : 0xffff00_rgbf) }}; + if(data.flags & FlatGL3D::Flag::Textured) + shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) - .bindTextureTransformationBuffer(textureTransformationUniform) .bindMaterialBuffer(materialUniform) .draw(circle); } @@ -2228,18 +2258,29 @@ template void FlatGLTest::renderInstanced2D() { 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 + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + /* + Colored case: + + - First should be lower left, yellow with a yellow base color, so + yellow + - Second lower right, cyan with a yellow base color, so green + - Third up center, magenta with a yellow base color, so red + + Textured case: + + - Lower left has bottom left numbers, so light 7881 + - Lower light has bottom right, 1223 + - Up center has 6778 + */ 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})); + Utility::Directory::join({_testDir, "FlatTestFiles", data.expected2D}), + (DebugTools::CompareImageToFile{_manager, data.maxThreshold, data.meanThreshold})); #ifndef MAGNUM_TARGET_GLES2 /* Object ID -- no need to verify the whole image, just check that pixels @@ -2263,6 +2304,9 @@ template void FlatGLTest::renderInstanced2D() { } template void FlatGLTest::renderInstanced3D() { + auto&& data = RenderInstancedData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES2 if(flag == FlatGL2D::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2289,10 +2333,6 @@ template void FlatGLTest::renderInstanced3D() { #endif #endif - if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || - !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) - CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); - GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, Primitives::UVSphereFlag::TextureCoordinates)); @@ -2303,11 +2343,17 @@ template void FlatGLTest::renderInstanced3D() { Vector2 textureOffset; UnsignedInt objectId; } instanceData[] { - {Matrix4::translation({-1.25f, -1.25f, 0.0f}), 0xff3333_rgbf, + {Matrix4::translation({-1.25f, -1.25f, 0.0f})* + /* To be consistent with Phong's output where it tests that the + normal matrix is applied properly */ + Matrix4::rotationX(90.0_degf), + data.flags & FlatGL3D::Flag::Textured ? 0xffffff_rgbf : 0xffff00_rgbf, {0.0f, 0.0f}, 211}, - {Matrix4::translation({ 1.25f, -1.25f, 0.0f}), 0x33ff33_rgbf, + {Matrix4::translation({ 1.25f, -1.25f, 0.0f}), + data.flags & FlatGL3D::Flag::Textured ? 0xffffff_rgbf : 0x00ffff_rgbf, {1.0f, 0.0f}, 4627}, - {Matrix4::translation({ 0.0f, 1.0f, 1.0f}), 0x9999ff_rgbf, + {Matrix4::translation({ 0.0f, 1.0f, 1.0f}), + data.flags & FlatGL3D::Flag::Textured ? 0xffffff_rgbf : 0xff00ff_rgbf, {0.5f, 1.0f}, 35363} }; @@ -2324,22 +2370,9 @@ template void FlatGLTest::renderInstanced3D() { ) .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); - /* Enable also Object ID, if supported */ - FlatGL3D::Flags flags = FlatGL3D::Flag::Textured| - FlatGL3D::Flag::VertexColor|FlatGL3D::Flag::InstancedTransformation| - FlatGL3D::Flag::InstancedTextureOffset|flag; + FlatGL3D::Flags flags = FlatGL3D::Flag::VertexColor| + FlatGL3D::Flag::InstancedTransformation|data.flags|flag; #ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES if(GL::Context::current().isExtensionSupported()) @@ -2349,15 +2382,37 @@ template void FlatGLTest::renderInstanced3D() { } #endif FlatGL3D shader{flags}; - shader.bindTexture(texture); + + GL::Texture2D texture; + if(data.flags & FlatGL2D::Flag::Textured) { + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + 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); + } if(flag == FlatGL3D::Flag{}) { - shader.setColor(0xffff99_rgbf) + shader + .setColor(data.flags & FlatGL2D::Flag::Textured ? 0xffffff_rgbf : 0xffff00_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})); + Matrix4::scaling(Vector3{0.4f})); + + if(data.flags & FlatGL3D::Flag::Textured) + shader.setTextureMatrix(Matrix3::scaling(Vector2{0.5f})); #ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES @@ -2390,11 +2445,12 @@ template void FlatGLTest::renderInstanced3D() { }}; GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { FlatMaterialUniform{} - .setColor(0xffff99_rgbf) + .setColor(data.flags & FlatGL3D::Flag::Textured ? 0xffffff_rgbf : 0xffff00_rgbf) }}; + if(data.flags & FlatGL3D::Flag::Textured) + shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindTransformationProjectionBuffer(transformationProjectionUniform) .bindDrawBuffer(drawUniform) - .bindTextureTransformationBuffer(textureTransformationUniform) .bindMaterialBuffer(materialUniform) .draw(sphere); } @@ -2403,18 +2459,30 @@ template void FlatGLTest::renderInstanced3D() { 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 + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + /* + Colored case: + + - First should be lower left, yellow with a yellow base color, so + yellow + - Second lower right, cyan with a yellow base color, so green + - Third up center, magenta with a yellow base color, so red + + Textured case: + + - Lower left has bottom left numbers, so light 7881, rotated (78 + visible) + - Lower light has bottom right, 1223 + - Up center has 6778 + */ 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})); + Utility::Directory::join({_testDir, "FlatTestFiles", data.expected3D}), + (DebugTools::CompareImageToFile{_manager, data.maxThreshold, data.meanThreshold})); #ifndef MAGNUM_TARGET_GLES2 /* Object ID -- no need to verify the whole image, just check that pixels diff --git a/src/Magnum/Shaders/Test/FlatTestFiles/instanced-textured2D.tga b/src/Magnum/Shaders/Test/FlatTestFiles/instanced-textured2D.tga new file mode 100644 index 0000000000000000000000000000000000000000..01359db39146a6fbbef7cafa5293338ead551265 GIT binary patch literal 2936 zcmcIlTT@g=5Z-f^g{r*ymVa^B-I;UtqCX&(DTxx>XVfFkVnm z41~lOQ>GNbNWc|EZlWf!Dh2YAs^lfVBHuTAc0C&MkW{&~U8kp~`|F3XtZ!-n;!7xKi~+FIhTh0zP-qrZd`#*;b9Pk>U`OJVOUdF-XINBMD3274Lo zeL)_38SGJh4AhN1ZFO}OpCqUUYW)!R*br{x@k40iPoYg7D9+^Xp6bnxT)vc!bz)fz zP5X0p_}ZzqvUt-Td{!sV0}oa?P&puXj|R3ji^v%>a@wk!4nYzJ@f_uqOZTnhQF*4< z%5?k2t`$6HU`Jw?f=k*S zDoRHI6FBs7=~!u)kYaSwm(4tT_A9t-CQ(SsD^B8Y!sM|4`u3FEH_Kyil?I|-Id|9U zXaUy^aw3-@_IO8Y=aJ7whlhaAj5%U|2{!t3c<;U2)f`3ds`*F{fBcw^(a`?5XF zjUSGW-L#`uUREa0pFFzX;e~m2S7+pwsQ>*5R+j?exyA5>P_#DC(u4&YN&y2r2fQ2* zSc@E#bFX_3zUW8!gRh)dESF+=4SOn2zt0T)c6$KmTrnjcv{3ai^R1meHquj;gtvk{ z5Jym&@8;Fe&T4mfM-@Jmvr~{!o{?0$7l)53a~r1Lv`C7uw_UM?rqI!^X*2zjj1m5%b_YvuXq%cp8_uiD&A&d z>eC~sKOTGujw_Y%hNt1G7E!+(-hCyo_a|nf8?+8`8d$ntj0y1_xNaMnV@^fe)%czR zwxWI^RMXFt*5yOHzG4#ahi(?6O8FwY%x8sHEa8K{U)&O6w7^4`(X;&J0Ka;&wZDBo z{+*xfTALb#gyV;U(zBz_+#Z+PVzS|}?TE9R!%P>0r-RKLo2(>#l_NctOa)|XKz4$M z2)aB7Ulu+koHuUdyA*XFH(=faT`Dm>e46+yspkoM8SGK6o;4dpJ!{xwRVXJ8W9HPk zNFIBvJLT&=pHA6`L!bO*5T8q8lfMMw6UjKp6Q412-(5H+I0y%AQrR&UXMEScftoWr z0nrg;c3OR6s$j#no1{u7hB$PK1{HpB!z<>XhZ+Xt5yjBYd7C)aG%MZZu<`h0-Y@Pz z73H>MFBmqI(?Sn6HWZ#dH5Re!#tjUb2Sf5BEZEspKL=tYy7$ga@6tB4{eM_92ByR<<=L>TmEr;x6eIztFU5?^ z9s^tgR7?-M(j&xmsqUf_SA43(YDhs%i*8xZ!X1!p$RKR-*Zg7{pOQ&?jnjn1V}3kA zd05IppO{g%alv7s4;?U;;xvkZip@bIP3g|-QVx5?JOn&U3QGEPyK(V=(}Z2i-qMBm zUWlvI1Erx`mfLGAbW<09Xwd=h2DM!n5a_p)_!Q2=Q!qc}`F7)6l>h!ZqqojBr4!ya mnrekn#(wC50mbTSVx7U7yt=wrGh*_?AfA8@$UCpSlm8po#?W5? literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/FlatTestFiles/instanced-textured3D.tga b/src/Magnum/Shaders/Test/FlatTestFiles/instanced-textured3D.tga new file mode 100644 index 0000000000000000000000000000000000000000..9ec1a93c8cb12a94cff6eed7557f0bc1e6b33e66 GIT binary patch literal 2609 zcmb_dTT@e46wW?@7oX&<|AKSk2_Ya-hPEhlxL9u0Qb6QViaLW7MhBG;2!sFu3WApc zf)wioDz$Rc87{35637XC<)O7R?bu(@?>otXh|`&NrVMMc&-%Xg?X}lgYaf&8h{^cN zHRYP#GX>+~;{J^dfaj>*O%}zW^1pmjPS`ED+Tw#7W|x)^1pjdK!@LCW*a6ZI)*T@6 znf_i_JJxPs{aCw!i7TN@xSTM;JrRodEK~#*nT+frvoBZz7RAWwhPkj7IiV)7x?psB z$ei5y5(u{<%LD6R6ewH^b!-?cGU=wPi|+HANqD5D!R!I06VHTc=F%VRykaMukx(raF{d=uN2f zB^^YPD=G?3;-?KJg*OJ-6%i0$?51EB9*i0^6V@^1i+22+8M9SsOrG`Dwd%`HL&pJ& zg_eKD`_lu@>-BLwM?_u7>S+AW8}hVI_H4;lx8z#CeAchz2b7$Ek{PfEXOpzY$F=D+ z?eyaHXbb=4SJ>w`t>H_rhrFTYH%y8~=Dy$qHbPZWz1NI7kfCi$`pE*VK|H ztf$cfv9H(0$KCa7)9%1*{~O=bAFn22p^6_oyd^K>#x02U9uhV~tZc!4POlLgSF8ot zPQ9>p9INjTU&?7vOCBYHsL#ODZO*_yk=X#E^hYMFv&VL7z-E(+-*;D_&Ki5MgE*zN zMc>Fyj^z4WSPa)_D+0VG{!q ztb{K0& z*Ee)0Su5#+2{pI~geZ}_L@nw{1eGjmz#3ct$hSoB4hf372?DDurKc<>=x>?>NANZY zHme&TP5R)(l3kkt$gcrGeuTs^bq*k#3xvfmfmb_0onTR47=)~}IlJ}*pg~}9glc^X z0$WYWPM=NI9z?c~WZ!B+mlKp>7^!Ts15P)1^zI`$Mzn!2j$4QI&>Mw( z7oLg#1u4pyepgN4R7iq%nWPi$BMGJ`;^#Jz%gsnW=72MPpt3eI?HQPQL5niJP0`3^ z#m({0H|2|dU{Nuxh>2ajmNZaRd*PEl95dOz4hGu?vXVc&E!W*%yvQjOhVbksgl6pgSUJw}XmXZXydUCzR7v<0R0_3F;)W2qcdPM~vQ47^@|{XgSU@gl`XeIjZepBw5KI!fS%w W37C_}n!~18S^HKPz0tS9;q~7PSt+al literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/FlatTestFiles/instanced2D.tga b/src/Magnum/Shaders/Test/FlatTestFiles/instanced2D.tga index a130de40ebf0ee24b2176d9c04a294f2af228c87..11dec3675a0c0b9b796563b0a1652e2b165fa7f6 100644 GIT binary patch literal 1058 zcmd6l!D@s+5Jc;oo40({{#4?cAc=?=qb?!&pnQ`1DwBm~X^#sl6tAlsTBm2E2VI_n z4tmsYRaKeZ$Qy0jTqyh2A{z^3|B5=YzEJirs3+?RW#6EI%zdEjk7y)wA1M18O=Ru^ zWuIlw&Hs zERyusT9GO-^2-T0!#^M-@=5u!pg~>KPa=Pe&vUz5XM`9{Xx_Jd&-*^-InQ~|xwnd9 zDE?*oU!$*Rnpe7%%6r=P^+MJjxlN#)R;d7ucB5( z?Ni36RZ(O9LwFE1&+d?+2Gkj3YwHX_?dL4OFk)+P6{ybM?=~ks>X4V(VkgTOQ&Tve zS$W7k(Pr<*cdX|kQj``3k$KfLW)1VKX?|u}-&(%%uy#A6m%FsH3B4NAuBWgF7pTHD zPto1huDRIOKNvs!do;Vs?Q@Gtd;HxZe!0B@_ygtz)4XnS*X)Sa)Qmu%u^akHyTl0~ zQ6o;ZtM4~NkvK^2Y$WDh*c+-07!}i6waN&Wz!K1I_39r)3kb>NvxT6st!utDdb~qk zW;=Y1f1|Q~v0C0UNL9xCkoIFnA8*qOQSGOUPgntI5%WXQ?Duy1YeXH%m5qdDXk&}G z(|~!-v~GFLY-qdDtzB`8;3iFz%I>+*nYl@-hBi_JoXkpleA;I#f=xyLtThXfv1nMg zJ=%uOA2OiSZuV%W<34#A3s6=t;?y?TQ+E0qM;vJ|&@W?<|2Spv?*ws4b)gN>XYt^m3A#XjbRo8M`npz$}&1+9Qb2-*?p7*1S zrGtjk-?Dj|5zUn_;=!`Rs&*}h;RQIlMW zrcRNWT&kHKu(E@QtSv)WrMS+|hAaBXC|?d`%nxXJSIhM<^lyzv)#1!;pxUucl^HbM z?f0UpkV?xwMu`71pWv?0aHb?Md>mMH)6`B>EA7$4qW#&^N?a z3J<|=5=DhPvLqrHsd!Rh&~zl`aoJ(5_C&0AnJZAC3R)wUhe0RCd<}-cNn3XHS=;Zv zik9j5sGjd?aFf@2&4Q?giDxjbVO9-g=Dk-rYE{&{S2=1`)R_1F0#K`<=KTer#$#dr z6FA?O?D`jsQDc{xU;7vT%A315S4@Z#jIY2e&f;7JW5$=@Rg9|`GbVp+y&g=61*Boj zF2h5p(B_Z3QwK>?LNN?jQjDhLGcZ@oAVn~tiVgYCbFd{F) zOGGE1d)7y*t~@5hJ3t6=G%3dmh=^-qQ%^ijY#I5WloKiWRJYv4of2^YUPd*Lm_Cx2 zIm(b~RjBQj!>*VY;w>o;5>6qW!6p`{lpF=Z71LN?BbHlJlY4h}_iv-+A9dfJ z;s!i1LLNxVd`dpa70G-aUPrZ#G2{O=#@zbCUZ*+#Ka8n?TG*zv+#$szb(X;vSlHt{ zKj1L*+rVwALQF|<0Fe}L;K(?+0*o^J$Um_uIf70B0mB7oVn=dKEKh{zr>5l7Qck*J zHYrD4@xG4@ytd1Mk|LJykC^aK1106_z)~_b1ZGB@{X3gjUY`(3Fd+^L@#YeuCE!9Y z4m$ID97HgpcrC*XWKxbL<;(bnaB3(45peUeS21SnJzN-5B;yshPtN#$59^;rL7VIV diff --git a/src/Magnum/Shaders/Test/FlatTestFiles/instanced3D.tga b/src/Magnum/Shaders/Test/FlatTestFiles/instanced3D.tga index c1c4d964064b9bcf88aae8d73c376f36f49753c6..0aa0bb4d55178ea989f21f5ac0fd9e78161f4221 100644 GIT binary patch literal 1034 zcmcJNOK!qI5Cwakop(7FmqHfd7soipB!q|@h$A^wV}nL0ma+lqsb0Sx$0d_nJH z2E$slWQ&2fY)@8_!IatW#teq_RCeIpoIbprdvxX;_jmp)a}UnzY%uM`%)K~|d$D$h zJ2E{?J016%GWTrloA@4j-^2P9t;s65Axm$CUy{`W!`=?f$^0L{u)bva!Iu%e{6=fM zJfSUK9#M^#pQyph57gr20d@F9dw9R8u6~a{lFKRI(mQcRKH5*+Anf)J literal 2086 zcmbu9?{8C87{||T$SG_!T{D4gy|?YUb-U+ohDbUCnL{B40&Hwy#!QWg!Ktvx26N!r zu5Z@0x4#R5!KsJ=jlPg*Vq$z@Oic8JZ)n*oU--xP{od|w#Snv$+!K1f-_P@$AJ20p zM4NE`O^GSdCF+4d;7P`Z;5@qd6~1nDW4&kiuMH^cvTk}h8Uf#?4zEhd3;#nu8Tu8BEaJs}l`Dc_T#-zm-kAkMzl@P1vsYayfuU9+ z^YnwgIWY7~7&e$Pe6fG);T0aG? zUxL@ISka5LA zYRK%{^t4jHPpb;TSl?E@23`cMpSg&TSIydEm}_9@U;B@q z*kh%cpRvp~4he*yDE1$+TNmjK^f$ERn{&y0p__ z%}X42X-&o>-Vwb_RMum>Gh!UVH*nYnUgibp=9jz_UJ%pKXVbdAGl$1`>TGbQUTxM= zk?~#85*aMV&9OW@2U~+E-%+-?vy&{Z=+u{}6ZejE=kXs=Vtihrtt-9`oKN98=iZTh zHkd3J`qVN^*KVEV)7F9mw*%ehZ#2>FELZo|``I!?Z(?Y`@nt~s&qM}@1{)vUdds*+ z+X$yJ`sxS#{RM^yg_p9>a?m)xL(K>$bI@#PoZqI_7a7Y#6XDgxJalTGQ4fXRr>=#< zsmSU=0T$&?o7PH!mKnPQs|btoC$xGaXN$B{>{2v+p#+OE_b@ZfvS6*M89)1zNpF^(m% zVHaHkx@-A#-^|zxM-RS&Q}jizu%22oh^5}8UtQ7PT`^`73>`?VwS?OU_al7%#PGRe zBd;DgGClgz)Zr5ohfYn69`Ai^!5B{%Clki;gmE-!97-Aon84mj;9NBlXcxx^jpvd) z(Dqe7vCLwZNiU2K7$Zp**6o39Ile8kb4o59Px5*wvDb<_GTV(3yR$%faUy0sn`DDh zVn641WVSS=&oOu(+U1FU9F(V|#FvtFWh=!8MCmR@hVt^6UL%s?Tcgza2LJi{Z+{{c Af&c&j diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 100e2a576..b4389bbc4 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -49,6 +49,7 @@ #include "Magnum/GL/TextureFormat.h" #include "Magnum/Math/Color.h" #include "Magnum/Math/Matrix4.h" +#include "Magnum/Math/Swizzle.h" #include "Magnum/MeshTools/Compile.h" #include "Magnum/MeshTools/Transform.h" #include "Magnum/Primitives/Plane.h" @@ -173,21 +174,21 @@ struct PhongGLTest: GL::OpenGLTester { Mesa Intel BADLIOM ES2 xx - ES3 BADLIOx - Mesa AMD BAD I - Mesa llvmpipe BAD I - SwiftShader ES2 BADLIxx - ES3 BADLI + ES3 BADL Ox + Mesa AMD BAD + Mesa llvmpipe BAD + SwiftShader ES2 BADL xx + ES3 BADL ANGLE ES2 xx - ES3 BADLIOM + ES3 BADL OM ARM Mali (Huawei P10) ES2 BAD xx - ES3 BADLIOx + ES3 BADL Ox WebGL (on Mesa Intel) 1.0 BAD xx - 2.0 BADLIOM + 2.0 BADL OM NVidia BAD Intel Windows BAD AMD macOS BAD - Intel macOS BADLIOx + Intel macOS BADL Ox iPhone 6 w/ iOS 12.4 ES3 BAD x */ @@ -555,24 +556,14 @@ constexpr struct { PhongGL::Flags flags; Float maxThreshold, meanThreshold; } RenderInstancedData[] { - {"diffuse", "instanced.tga", {}, - #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) - /* AMD has one off pixel; SwiftShader a bit more */ - 96.34f, 0.113f, - #else - /* WebGL 1 doesn't have 8bit renderbuffer storage */ - 96.34f, 0.113f, - #endif - }, - {"diffuse + normal", "instanced-normal.tga", PhongGL::Flag::NormalTexture, - #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) - /* AMD has one off pixel, llvmpipe more */ - 96.0f, 0.333f, - #else - /* WebGL 1 doesn't have 8bit renderbuffer storage */ - 96.0f, 0.333f, - #endif - } + {"diffuse color", "instanced.tga", {}, + /* Minor differences on SwiftShader */ + 81.0f, 0.06f}, + {"diffuse texture", "instanced-textured.tga", + PhongGL::Flag::DiffuseTexture|PhongGL::Flag::InstancedTextureOffset, + /* Minor differences on SwiftShader */ + 112.0f, 0.09f}, + /** @todo test normal when there's usable texture */ }; #ifndef MAGNUM_TARGET_GLES2 @@ -614,6 +605,7 @@ constexpr struct { 4, 2, 3, 1, /* Minor differences on ARM Mali */ 4.67f, 0.02f}, + /** @todo test normal and per-draw scaling when there's usable texture */ }; #endif @@ -2833,16 +2825,13 @@ template void PhongGLTest::renderInstanced() { #endif #endif - if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || - !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) - CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); - GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, Primitives::UVSphereFlag::TextureCoordinates| Primitives::UVSphereFlag::Tangents)); - /* Three spheres, each in a different location, differently rotated to - ensure the normal matrix is properly used as well. */ + /* Three spheres, each in a different location. To test normal matrix + concatenation, everything is rotated 90° on Y, thus X is now -Z and Z is + now X. */ struct { Matrix4 transformation; Matrix3x3 normal; @@ -2850,18 +2839,22 @@ template void PhongGLTest::renderInstanced() { Vector2 textureOffset; UnsignedInt objectId; } instanceData[] { - {Matrix4::translation({-1.25f, -1.25f, 0.0f})* - Matrix4::rotationX(90.0_degf), - {}, 0xff3333_rgbf, {0.0f, 0.0f}, 211}, - {Matrix4::translation({ 1.25f, -1.25f, 0.0f})* - Matrix4::rotationY(90.0_degf), - {}, 0x33ff33_rgbf, {1.0f, 0.0f}, 4627}, - {Matrix4::translation({ 0.0f, 1.0f, 1.0f})* - Matrix4::rotationZ(90.0_degf), - {}, 0x9999ff_rgbf, {0.5f, 1.0f}, 35363} + {Matrix4::translation(Math::gather<'z', 'y', 'x'>(Vector3{-1.25f, -1.25f, 0.0f}))*Matrix4::rotationY(-90.0_degf)*Matrix4::rotationX(90.0_degf), + /* to test also per-instance normal matrix is applied properly -- + the texture should look the same as in the case of Flat 3D + 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}, + {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}, + {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} }; - for(auto& instance: instanceData) - instance.normal = instance.transformation.normalMatrix(); sphere .addVertexBufferInstanced(GL::Buffer{instanceData}, 1, 0, @@ -2877,31 +2870,9 @@ template void PhongGLTest::renderInstanced() { ) .setInstanceCount(3); - Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); - CORRADE_VERIFY(importer); - - Containers::Optional image; - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/diffuse-texture.tga")) && (image = importer->image2D(0))); - GL::Texture2D diffuse; - diffuse.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, TextureFormatRGB, image->size()) - .setSubImage(0, {}, *image); - - CORRADE_VERIFY(importer->openFile(Utility::Directory::join(_testDir, "TestFiles/normal-texture.tga")) && (image = importer->image2D(0))); - GL::Texture2D normal; - normal.setMinificationFilter(GL::SamplerFilter::Linear) - .setMagnificationFilter(GL::SamplerFilter::Linear) - .setWrapping(GL::SamplerWrapping::ClampToEdge) - .setStorage(1, TextureFormatRGB, image->size()) - .setSubImage(0, {}, *image); - /* Enable also Object ID, if supported */ - PhongGL::Flags flags = PhongGL::Flag::DiffuseTexture| - PhongGL::Flag::VertexColor| - PhongGL::Flag::InstancedTransformation| - PhongGL::Flag::InstancedTextureOffset|data.flags|flag; + PhongGL::Flags flags = PhongGL::Flag::VertexColor| + PhongGL::Flag::InstancedTransformation|data.flags|flag; #ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES if(GL::Context::current().isExtensionSupported()) @@ -2911,25 +2882,58 @@ template void PhongGLTest::renderInstanced() { } #endif PhongGL shader{flags, 2}; - shader.bindDiffuseTexture(diffuse); - if(data.flags & PhongGL::Flag::NormalTexture) - shader.bindNormalTexture(normal); + + GL::Texture2D diffuse; + GL::Texture2D normal; + if(data.flags & (PhongGL::Flag::DiffuseTexture|PhongGL::Flag::NormalTexture)) { + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + 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); + } + + 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))); + normal.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGB, image->size()) + .setSubImage(0, {}, *image); + shader.bindNormalTexture(normal); + } + } if(flag == PhongGL::Flag{}) { shader .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, { 3.0f, -3.0f, 2.0f, 0.0f}}) + .setLightColors({0x999999_rgbf, 0x999999_rgbf}) + .setLightSpecularColors({0x0000ff_rgbf, 0x00ff00_rgbf}) .setTransformationMatrix( - Matrix4::translation(Vector3::zAxis(-1.75f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(90.0_degf)* Matrix4::scaling(Vector3{0.4f})) - .setNormalMatrix((Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)).normalMatrix()) + .setNormalMatrix(Matrix4::rotationY(90.0_degf).normalMatrix()) .setProjectionMatrix( Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) - .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) - .setDiffuseColor(0xffff99_rgbf); + .setDiffuseColor(data.flags & PhongGL::Flag::DiffuseTexture ? + 0xffffff_rgbf : 0xffff00_rgbf); + + if(data.flags & PhongGL::Flag::TextureTransformation) + shader.setTextureMatrix(Matrix3::scaling(Vector2{0.5f})); #ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES @@ -2951,34 +2955,39 @@ template void PhongGLTest::renderInstanced() { }}; GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { TransformationUniform3D{}.setTransformationMatrix( - Matrix4::translation(Vector3::zAxis(-1.75f))* - Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(90.0_degf)* Matrix4::scaling(Vector3{0.4f}) ) }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { PhongDrawUniform{} - .setNormalMatrix( - (Matrix4::rotationY(-15.0_degf)* - Matrix4::rotationX(15.0_degf)).normalMatrix()) + .setNormalMatrix(Matrix4::rotationY(90.0_degf).normalMatrix()) .setObjectId(1000) /* gets added to the per-instance ID */ }}; GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { PhongMaterialUniform{} - .setDiffuseColor(0xffff99_rgbf) + .setDiffuseColor(data.flags & PhongGL::Flag::DiffuseTexture ? + 0xffffff_rgbf : 0xffff00_rgbf) }}; GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { TextureTransformationUniform{} .setTextureMatrix(Matrix3::scaling(Vector2{0.5f})) }}; GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, { - PhongLightUniform{}.setPosition(Vector4{-3.0f, -3.0f, 2.0f, 0.0f}), - PhongLightUniform{}.setPosition(Vector4{3.0f, -3.0f, 2.0f, 0.0f}) + PhongLightUniform{} + .setPosition({-3.0f, -3.0f, 2.0f, 0.0f}) + .setColor(0x999999_rgbf) + .setSpecularColor(0x0000ff_rgbf), + PhongLightUniform{} + .setPosition({3.0f, -3.0f, 2.0f, 0.0f}) + .setColor(0x999999_rgbf) + .setSpecularColor(0x00ff00_rgbf) }}; + if(data.flags & PhongGL::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindProjectionBuffer(projectionUniform) .bindTransformationBuffer(transformationUniform) - .bindTextureTransformationBuffer(textureTransformationUniform) .bindDrawBuffer(drawUniform) .bindMaterialBuffer(materialUniform) .bindLightBuffer(lightUniform) @@ -2987,6 +2996,25 @@ template void PhongGLTest::renderInstanced() { #endif else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); + /* + Colored case: + + - First should be lower left, yellow with a blue and green highlight + on bottom left and right part + - Second lower right, cyan with a yellow light, so green, the same + highlight at the same position + - Third up center, magenta with a yellow light, so red, the same + highlight at the same position + + Textured case: + + - Lower left has bottom left numbers, so light 7881, rotated (78 + visible, should look the same as the multidraw case or as Flat) + - Lower light has bottom right, 1223, rotated (23 visible, looking at + the left side of the sphere in the equivalent Flat test) + - Up center has 6778, rotated (78 visible, looking at the left side + of the sphere in the equivalent Flat test) + */ MAGNUM_VERIFY_NO_GL_ERROR(); CORRADE_COMPARE_WITH( /* Dropping the alpha channel, as it's always 1.0 */ diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/instanced-textured.tga b/src/Magnum/Shaders/Test/PhongTestFiles/instanced-textured.tga new file mode 100644 index 0000000000000000000000000000000000000000..53b29b425f05c492d7750f0ae6d253bbcfa3fae6 GIT binary patch literal 6512 zcma)A`BPNck_L*}_kG{@TXpMJ0kQ~+0(GAT)vwEeF% zsWi!u^oLTZ{Qu}O;skYnms$5)=E>hG|Kr2jzyE3NuixMMw{KT|f4hjib+RA)_Dkx+ zH=&=u3;pta=+_@u`JTBmi@hc29jNr|H)&Nrq*s5)sQxjd=BJE*eSa77$2ZIW^7Yd1 z?-u{*t2=*wGynH5Ny-Wd_kZys@a|Rc{TIRSz6yT-mU{Op^u>$NyI1t)y;yf!WN{WX z5rEKzg!0PYM9;j9o&7our79rr(*QXJ<^SXBTmSHC4(PwVxcSS|sSl6G->nTHjWi%| zKfLFCw&{EM#Q*vks`|fr8F>3Sk$11gp{dlyGSku!e7Nd+^%U{96RJA#ls@?xbLw-Z z>=je?I*RJWuMhqDeEQdC)4x2M`uXY9PfsR4Jf8UB(b#tnM!sGj{&HpTvxQz{6T&Lz z@@@CVUC-v4XKTaz^r3I-esFYb}{8z_rTzgoTi=I+4fOV?g3^lZ&` zu8p^DW7ww09Jgnj%k!?)Mc4YW`~C{1K}c=h^E_VnJl^nbu9GF~=Nq1<>z;?J2mo={ zDmm0!3Dp(N`424y4&MzHuTpJm)RXm4+eYa4M(EAm{x{40ub2B@EnP$Xmy3O$E%rWN z?0!1m^?2s$gQ<3KSsK1H+jjv-b5OBmu-7^^1YY*JNyq$6=bc&4(!BfbZP&^I;dpP+ zwYD?{6}TQOxjrnp{(I5&{*H5X!MQZ=SeUWTOeBB^n0|MUYu|*oaK=}3%ilcj|F{tN zcqh;bqKkp8xlSUposVa)KAP#+oauPD)_(slSJu97U7c!K8g5z`yg1W)9_bk>e_R8Mx+PIlD-Y6dDX_g*zYy4y`XSFQcsb%T8$hx@-7 z8H|lyw_&6cH;7D*j6sFAsS(@!i0$_qwvRWge;u+;UPoh=fnHO02cWhY8e=uqNU=3~ z%9h{nsvB@!xb7|*a)a=mVfU?T^^m!17v}md%=VtY*>i5PulDrFp7D=0BmZ1A)Lk*q ze0sRGVx+BVw7q)xN(Er2p;BWhqN z2`tz9CZK%Fc&}xv*L=U*JlJLG=|CQm5)=td)2=o2{2^qkI)e+-R%3aiwyowc>iqne?dl+CX{Fz^Ts0(w>W_u3ai0Y&moN@>xiN<(RRp zMPj(pY`EO0Z)z}KJWn?@&@GLY)+S?HGl-)JlNC-PEmja2gG##_PIlFoc3vp$ICp$k z#NS$bthxGVYfW)`T}gfU0Z3QFNig0fYG`RRkZO$=boF(*x@uibrLL-4cf3wta9*EV zZ^&pc&=(CQ~F>T zk=oLN+S2_X2dO^3ud*b+qIfUJ<)w#m)2JBbK9ZktcyC6)sWm55>SHQ(u}WQ{)*Q!DttnM&PeR0M?Wwp$Tttq?Lt&#)oxTdl z+qc3TI%{y{YOS=|o~w2is$B=vu7eu)A&vX6#&cM6@<0xx^g#BBqU_^^StSLTNA{*? z(B6U^=4d`)TUC;ep`Slhgaf#E=Fo+*q6BTBCRVQ1Rgi&^;ev3fN>idzA62RjDwKs> zg|Yw-h4O$>1v#ix9U6=4K|Y-9)_!auAx?WIXAIU|pmpw3JNBz=c}h!;!kVkJ<|%D^ zh~(t@i*`o~AvsJzb_7>pe|F?RUJQiM!pVa3z||{mPDE^mM|Q#B!Y)n5@jXvzyhv_L=uzAGSSB; z?mLK^aV4zTN=z86!BiKPsQqGqjzYrt)R5eYcc!=9|DKR2Dq&!oZr zU|t&TcJcmQxID)SGK=zawx=!g<+10CTTp};0#-pnxWOyO=Or6?CSi2)w@@<_snq&RYN;^Z!1wqP0)OK6ce zC=xkELbX7k;Pa(?o`la6i}*Z<7$V@yghG`_0Z*NmF?ej#MmE$i!Ne+@x3A%{q_W7yxA-p^RbUs$|CkxGfOSPjEGY z7>=G5Fl5L$#gtIw!|VhCE1$0gFD_S{nkwON#H>hCQ&CaJ=bHrr2WHKZWq^=iN?x}N znxF&+uW$&ZW`S786DYYL$l<3XrzEAMqzL$2kX6a0dbI-c1zESvi1|_;8|`;6L1&C| zfwjZoNYFI!9d^_qg2NX1+4FYs1$I8)!s8jZTuo}KjKdM6q@*S%B_|~%r=;*X957XK zxjG)-gh*TokthH>a_oeoK;aU~Z33x@FVgY^ic~JzK;OcdlAa0uhIOK1ngfFlO1WpLc$%>LZo}Q(p01tGWoSd4#6@XrHG7qy% zO_d{r$20QzR-^)la3inITym~zE?<_)6>>;VNl8;sVQMNz$md~pN|{8Xlf=9GL7Wh!IVCB0ngWuu1BWCs~u9Hn(9f z;kke(d0i*=E!cMxPqHRIwg`3d(}Fz7`i~v@47R`@d>RbBp~7z|`fDQLw^+i7J)&Ds znb*G&2)&}{ccI9)VdjT0^C8UqiB1?lz=tKWV zu@e#I0UB@qJwzsik;1fo@1C> zXb%zbnIZ?7$Oj4l?|!KU*NHy5PcmCe@VE~Z=#XmbfzwyX=0eg2=ka` zwisqMBXTAZSq{-848cf2BGLf@D4>!kZ#ujlrTfz( zC+Ns}h#b#*A~fVpByuYn>54?I1;ZG@OQawX=>P!~0D(*XTY$y4+WH-?Ntb8A)sldZ%bsV1&O2NA5*(dYB{C!M*qV;pa#}BGLf@C;%q# zNe!FX+652lIz97l?_Koh4{R0&x1I+dyrvc^LKo6QO`*_QFhow5NceLq{DhA7Mb6ZR zzvvBow(L8&pca*Ob$J%k5ocd@Dn~wLkmsPXr!h2R4Gi`CxFE3Jy}iK8k|$!wyh4{kvCv)Azl9 zx$n8L=8mnnyo*Q&2%rdaYj>gw=|^TVUqN?dZuSAYbJXFQMzG&=C*QkV>|4$Bt@-^M zUjKc}+#7f}4#h|N0dWL3JlSiWl2!NqWfz4eP+=MXfQjP)82I-Rwb(kA4zuMdZiLl- z9UUXy<(~6;Zu>lo9`CZ-yW;V!;$U&#$DuOTT*ZvszGYWn(dEDG^vtnE#sB~qfSD~F ze^bY3zHG9zL9k`9_1Wx0cE_m0If;*u(>>?%%;Vl*=$Ixx_v26*OgP}iaU%qQxK~I* zDga0Y77xHX`bjpLnvCX_F{p@bs?Ta4#DGa(q}a5>HG_i%9wsvm6|v?l++@=^i9m!S ziLG%pfkj|{dUDZR0N94a)Ln#uI6ok+r^#CfvChHQ>u;Eh9ttT{xW?E~cA z0^Qn)B%~%#2;dHUN7Zg^VI=0VmFWz1I(@C)cmYhnhdrmOxG-!ne5(3LN%RNK30TciM3-BGaWaXLa${Y=xnV z3Bq0?5fB_gF0lcG*<9M11p3_;H)0t%J)GlHpvLeS^p6x!d zD|941{Xi_ID4JKmFx))9EI%kG z5|CpHOWtm;H0~{+h|{+FJ>2Kd!#vDA~3St z(p@fzB;6swBg-R2ey&fJ2l2DwlVJ%bwh$ml#JNf8Ck#L)DvY_yBJOcKww}AC$mQiUucJc1^n z-zfANgf5-HqZh#N8Hfi014}ru1p^)gBZU*s1Pss!SMtgvacTu_9T7x3w0xVIZ&C4# z3Z72JQ%i{$6l5th$jJkHh(H{o<7vnT3?azA#p&f-ladEPFl?=qtCFP3MI1ht!%O9e z`9!dU6I%#CSUetff)aTnR7nIJxsZrV$bl{5r|`Kc9QJh^uiUAeWGvyt76RgtuoEoE j8*CC{NdlXaG>v!R9UknHcUJPYJBtnS-1(OLxAy-6r!L1` literal 0 HcmV?d00001 diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/instanced.tga b/src/Magnum/Shaders/Test/PhongTestFiles/instanced.tga index a761d9413f319424c7c5e68e3d4a2cf1273fdc2b..8563565980b98545f9328f5c0593e90f05566135 100644 GIT binary patch literal 6448 zcma)=YnYDZ7RUeZ%sXbr9B7Ck<9z7sP+Wva&JmRwQgYm#C>0@MzYK$MHYmj~wj!r$ za+nO2aVCezX}c)4U1@*X*VSkHx7PE#yRkoQ*L{1|v+lM2>%Z20zwdjmkt!?xufO^$ zTPN?hXgo&)%o+fZ~?68%oGz*oI7VLr%&s|2^~42Z@$525i>>KN=Kv<(rM|e zbWS>NxP-=^ScoWCIc2fthcinD4j9n!}fZQi8a8}+YG zb#c8uEml#HR<6{tWgz?sCM}azN<~U4R`d1hyg^wT71?A&ZBR)lFuss}bZW3;Pd9o# zYotPBHzx#4(SijQVdcu`s#Uo~MWt)k_AM?>Ubjx`*2R1l7pH0MTCFKk$!gvDShqgZ zr4?GXR0|hs{(LQ3!~kqIGp0p+tmjJ$m9SJ@R_J%wf2``O6)BR|O2yJTj`BJR2j82{ z?%%#bLZr2zBDG}Y72(X&P%+wrdo-|jQE#)aCUqcJDc7fV1Qgn&c#KN=dE2Ix#_<@osuzf6c zxO$KOEX1yO;vQVYE09e(-qB=EkTW~PZKrcALF*0CKwUhllE@#8dc zqUlL57Gs1o5tVMCrbrW|af-Yxjh7}!lcdS6hs9K*Lj*P&5d|x(zbj#m?bO>@5_arZ zz51$+(WCXo8}|CvTkxL2%rp@NY8GF6O|QIShP;v&bRi0R#<+2?%wuLk%tA6C2|c2| zA-yKOBE2q+_Sdn}Ted_Dcp{>}ip5ub@H*jko&m`bBlO~ndg&z%8z$tDBQ_8Y`pnqNHj%nS`lp!pWu*tSsEd|#J;|04-ND8kLFX*}F^wd)tFhI{ftK3{00|&;KRZL_NHH%L_t!JJw z4IV>>T1t?n($ss*7=(~+6^k^+ON0PXs0@`}kOoT6NKZ=xq-Uk)q+C-HiJ8l%2_;KU z`4aX{8q_~e>hR$q-G2SltCzZUQ;!}d^zJ=_88NX(k92kKZu&lb)W5%RA2cYWOtzy& z*%&q~BwN`^G|C6j0-A%IU4N;M)I(8qw}*Q9=*@xcO?i1k*G;@|F|kzOJN)+YQYL~2 zOdo$-9XqOBJGF0bqeF+7&-U#zO!>$o>eNYHyBhz#eQhsr95_&e2ODwDNN#S(H_b}M zeliAoK`9ya>g(jXN}Z%fl&E(0POH?$ZMgFcS3OgMpN(`|mgDp@-DAtrvS zY%lvy#RA3@@G&E)w$ek=14_{S_F$yx!B8?)3#pA8c9D8YPa0BqGVJ#=#r7K#l(uzi zHE*ta?op#g%E?jPx-n*&iS_HNNfXP4^@ks}y0axXQFlK@2^=AE0M335tVkKQy}?#f z+s7Bvy0c3W$G`?c z73ogtE~$ZAaxz*ON85Abm|`2vrq-)xEUHygMuw7;6^Tgfi1-zXMpe18@eYUv$Dnaz zHEn9e$Gd4$aO@i3^@a}YvW~^rK+~?4rvpeoyzjMq>8Wm5GBdP zG~ZG-_AZNS^|0H1q7{M7Nle6cHZ$48N|me*>FKt}w&5AXitueg8cB9C$c7{<$y9-B zqKRxnhM8MZ)(k9W(E{1i-tA^LA9*p)1w5^FGPwYGY525vyPsMGJ`A7sUa^^5$2+l( z`jzyO-bJ|^CB{wB`WX&O7=#g5?Y2E7SX*xz^SgjSsyq z{3N1HiP{!bNyJ()*p~XiE+e5bJUHVhb}6!5(Gn$Y^G29jtaSij00T0=LL73+^HBM| zv?T1N^*8^_ZT8*#!jI!u{zr-J{tXexB*D^F2{S&RxD>fNAQr`kH6ZbvBGO-J&SK2U z&IQ8{q*YE3Z_2Sz)(eBTg_O2ng@^(*iGLB`i6{3*QWbgl`dFM65$#60_ zkW`Ufg(NT>QSkU2P#*>YuP9l<05gnn!I4}5nHtBOTp?D1_iyh>@1=Q5hZOU)=eZ{* zl+6X`BS&0CPODN~M1h(`ObCHF<-+y7XdDNq2^AtmyeAru$@pyP9dG5J1hLs(2~H3= zRAp(re}_EayrN_Y0}6(5kjq2|aXf>*J<@B^n`p=K;G6sn9^N-XiJFqcOs6}?O9LqE z83w5~|Bp(P9yjLF=?@BQq z3JUn!{IN^52B;JeIzhrjK88Je+)3gIPk*# z#l$$)pbBx4yt{bTyUSef%?2cUmo&gTrorwr)YBY6y+&X^OhN%I76;N>{MY`uI0|rm z!x;9fLoR)!e$={ORS)-sHwvcn4hQqV6*NXb1Lf0h@C3YjN%-^)K@1Pr_>Wp8Z#BAm zli5Sofp(R;r28Gei}#&99ig{_fP(-a%5K=D#O-jZh+z$bMX(=xBDC{t^Z zem!p+wvN_UJn93XLtp`RYj4-uJH@~oqjq3gVeE23GOaMn@}3RrhvL`$`@Exj(0h}X zu596Lzysc@pht;uYs%BcUoRMzy)4WHgW^Z zFb=&yF@E8dF2`N4#22}?RMW5XncjAA<436GN|rFd3}Zs%gpBNZ7;)pQ7?*U4hf{`K y@x_^9g@M2;N|xNp96}CunH8N*f_E1DZ-1n-e{l`#c73&r?;O_QtK+Qz literal 9748 zcmZ`<2b3Jum7NY%)m0s;bIv(;PfwOKl4g`O3ZtAxVU$KW%`nQLdPWH(0Rkk1K!}V; zB1njAF!-=Gcrm*MuWi<6@fv$=Y#)0aFl+4I`>JO^_ImrgOa1!w`@Q?_d%vcuL`{uQ zGyLhS>8x?qyk|0*hVdfCO8)brrZS4JjFJF`uZ-ZoC<<@Igb$LM%1A7@5x52zG?f-j zr4h#eDkt3T5iaTJNxo9cSL*mmv%Fx0rZNJnj{#Q!wWiX*zZ2K|Jg2ENX)4WS zU=Rxja8zIlOO61~0W4Q3ag{o*QZHKrzKLKr0ZI(HN(0s$2A%~N?hgg-&qc0MD}RZ; zR|XE_n}|>^;Rw_za22Zuf!hE@ruEO6(SK2AM*kHDoTOVn$MhLM!+w%wf0^Tco9BLC z;65vIpBKshU*NwMo@xi-iPu!>bf8=rrKe=Uw3hp8E%!x{`>X&L1HdhS&fGfRJbFQT z)E{O0EIkT_@gLG7f1eup->DJ5Nw$27b*BLX``r}#QJVcYL-GGB$9|gQex2ujljnY0 z;C@#iE=Uyi8Ih6zf&ZV9tI|HUvTkN&SGdw4{Ha6>f1782m1lpJh0ii@3NV^lW|>CL zF^`&?Z24t!#II!gb#eskS2WY|X|m-PiRParnm>s*eH?4}A-;1n;AOs*VBblw?ntq&U`cFlo=_fcyfCe+56L0*F zc;k;iD%SA*X#EF~y6@ndl$DlwJI;JVHqiWbLilbr^NSg)KRG)4Lcec%J3k`NHf1GX zfNjodxt5$^Z2ibh-B&*O{+}zAL$5#VIWm@gC(VACV*fS8zMFtY2{-|;V*O-e!&GDA zG+3=k_dCUV8R855Y- zW6Y~j7n4eHlg&-jN^Kw2wtWjvAxP~nYG;q^qrTbY)( zqovmZxoZ&8pSkQyJ?2f^4`0UtiFq-qW6nmvlgUP4`nq((lG*e2?xuJjL7GR-ggQkT5R2R(_4p7;e% z;$C>8+n&H&i7?&7btqgq#APCe+LTyI^B|fIDVS6U2MM?dd{`WrRFFvq)rkPc@PQ9y z?-4vD-ci)0T6F1=wz~0a2hUu6x3X&6Z{wva{`8Z+A$!6Vy~FI@=u187OWxPi{>pQ& zR`A)j`vF&v&j__&{m`P8tuUjoXY zq*5mX%#Z~o&}4CjIkJRoaX*%-tKY&k1i*(9;I*-OAyBK0H|P>gmcr=l^^+}jyzgmqINIpgK%hZKPNeC5$ zNUb(n(#7iy$tGv~Ftn8I4hfoz7&=17&F&30|2~_4zawt!i}L*fmQ_6^Ypxz$0JD&SNHTo_2eadpV8Q3q)rlXM9KaQt$hAYOhA+7E~_1 z$|b5?CY9SPn?>b@VT_sTBu=d=msJ87mB&U2p$gC=>P0l#@hXiy&HFRRLgR@u8g~o_ znCO{@7O;4>4gjJ&+-!Dl!cH~2*PGGlt{#kW&Fby7`gY36pem@%!J#zDsI*DuJ=7N} zKLoWweU%5id6iSEa_CexgVJt-smRP^4zmO(0j$btK@QavP)WqVhxn^C&Qz5= z=ZbR95W_h_IB1=tjm~Zp*~&5?VrX=%FglhS9ZQY&E~9-3PEF3$X4g9W@v(Te$%*vK zb=n2LR1I^rmF4zz%FRRlK<2ioTo#oRWJQ%7N$^T5uQbE-O0z*}5eI=9rCG#`0SBek zNGT!*lmlP)JK!B3O2Eg85eUw?BB0IM1FX%*T74Q@(qNrqu+BBuVDn^~OQ$qB%V6m= zSZ3mqXkUbUC{5QIDvWy*N`hp}KKk8AISioY28xO56KgB6fV>dhW>i{5rA4PS@k%47 z6k)tl)C~fHQqe0lGdmv zq6S{6V-#8#r_gE?T3!Np8tQnZemD_)!)m0ia#|3Aex6iwsH*;D0!F*qV8xxAH#7@| zX0u>u!2yPIn$CJB~Bou^croR*Vj{CVyo8Jj#>>TG7Akxx!Ah&_N{PExBLLL2_z>&zf_rvN>f$) zVe&eV*C=>Spp zSF>)0^)Q^rNF&a}YC;?z=d>A(uE^^fKe7GMyVKj^i)tD!%47zR|z<$Cj+AIW{eKvu9vwBqKen4I8n_y zRBDq_CCH{%F%~uJL{6+G!U;)@HmlLq$~iPsSBYb^#x|{Sl3-3;m0$o}B_}9ZjZ$q; zDtU!M1s4j1TB#INYEi}5WbOSdPrONcZGqR7KwaJ`lQsHra7VW@1xcjkb=Z2;($v*e znjEz=m{j#0W_lQ5xPQP@%fr_&O$~ZMCFqqJt&-!F45v^t3ME@pqZ~?Ar$EQ4v?{ex zO+?W&J~Ro-$B3(t=Cm!GmY&wBP70!-VI^Rg5jCtDtx83gJd|i%t z5+39;wF;}~CP>elDmhhEb;)ro;ZPHWQd4awrP@lS(qknNJA(z(HT0}`8e@8*rz73x z=;`=IjMYzUO+LFLb+s=&Zp6LW!V40>Wb@ZD`K#&Nv+3+piS$EQN>p#ba+2&;fa+v^ zbW`H#?WyN_Q_tU!exW}-w&jsr@#S3c71>_S6<*F3UWB_${`pMqxpek&D)U%8c>&)h zFDKxENZt58Mp^dKwZ}5+Dl!@>en>=jBxP ziDc%{MCyT9;(R>11PsCN}es%Q0_3=xai86GB6krW$x6bO5{k#N+Wk8fgfa5QH zUnPBW&wS-R{a`$KZ!~^39HsjaNlvQh?(B}-*AshqL;Nv0$mQ)M4RUz4H``9tzeZ#W zFO~u71A{CFxL?iW;s4n*1kXMN%_##4aWR&-D-ydc7^d6mJpgr8__pQYyH`i=TStMA z%@bRaPi>PE+L1c2aJlU5DkPEZ8bVaZ)Fl)MI?1`@o<{RQEtDadeguLcBWM~WayLM|tv|UqcxFZ9o;6Wqhdx5pksuno8h2LPjqpj9zcL7z&O z_K9r%$wKYrV(D_OfXELfQuoOzLd~VScZsq0dnL+bNq;W%hJ#tE5hei zlD-K3!20-8y~X!#@BHHRy_fHLe^J*DT1LK<%~Poc01543M$J;0OPT!R4NX_3OnZA# z*M~D_zthlkIhjVT=c9=`A#EgfYbbI8t>X_K^!a!Bd6{l9A+`)W3iF1eJ5qBUI_Dm#p8e)ecHwMFl{@?)+s1|vcfU#dDaBM-~<){FWxFwDxc%6#J9gw*|6!SO(U+PvyY&d1AvVhDE%NTnSL-=ywoxM z8+YAP`76D{s66&WrDOVQsm%FE9FpH6XMZ#hD*J=`y#Aet@9}O!>URQkgEaL`^`Q2L zX8VrJ58SjU2qjK03*WIKa(+$X+M%|}yC*7@kKy{}TPstizm+edckf5<4gfYPFxkaK z>S8+gz}WU{(%_$d@>62*#+#MJUGHS`=OeLOu#-ZOT1oEX=)A zJ<#I7EFan)a-3K^MC8s@@h5k-{P3Z*m2cko=xtx?oc(sG0Xy{~8cVi&C2UyWf;Eoz zCc8V4x~EurXy&Xpj~x5_$)}05(Ee`p8SWN?fP}4V!TyRS&czaU zCQ|2$rTg0_JU4g#o6|dAZfwS8M=2meHTnTrhdziP+i*eXPo{C}B-ggdZqPtkAV?WB z<`WGR=hTwW>E+S0tCOde*SFLijU~^b$*{Z1fFc@4!=II^IAfvakKZ1R-xf>Up30ob z70zYzXTc&Il}kYlanKjo>-G0T0Q7`xTV0-w;BXcoF6Q+Ud_5EVJrjeQr-rvrkKWK3 z+dVtBZ*KhH{CIgm{P3dqu_f`|&emM|NHTFU9=|0TJB^(liQQfXBn*Mm25iDGwZYBU z45&gRb|M_TF%&t9T@eULv@wXsPLHqG?cE{)O1F2j%d_D+CP`Q8M%U<#?lBuZ<92u_ z_4_*Z_-E|*&px!Svwq{O`pt9dx6Q3z-7$VzOZU{4)l-^#CN^!D(70(_!`3l%y`yUT zTZ+3H3w!Hx`)jj@(#a#S*o`6T%A2q^Ly=>+FHw7j%IHvkaKBufDxIxv&n5{_x;-0R zp7qx;l0YzyDNk^#H`?b-?DVDg`U?jFwTA-rY;?;;H4erd>N;hyBcs${u4b!cho zz@qfNx#>Nf>750Toa6WWR#kO|#|!6`tT z{0sN{%KQDWfdl@*gJg#fl>rGun}6^C=CQyh?eiVl>pQU9yMLE=Z@*`EpJ(TGZ{KG3 zj?M0^J)Vu-o}R_-wTnD!7y5eU1bSu!*G`SB9&;TJ$>~~UaJW|DP-TN2L)X*(A0o8d zx0t*mhVS4W1d{-z9G61VnT;+Gh27qL#KODh2G1@K+2QWn=H9WzwQZwo^LpoowT`tb z?JJkrS1os}TH#ntW?#A7zJeYLhqsmFaIO#?&gBm0GABs6*P@{4c#jtnffQ7_!Q0h` zG!etQr=NoD+c^kG7>X1a3he4dY|jnb-F;i31BKtR-np^Ixo)*%O}D*!8A94uEVXwp zl>nuEd6#YJqU$)6?arm5-O+^u3FBUa5~6QF1AXT4ZF4W(ip1dG)4!bpk@c1V2}7f& zzZ!a*yBE>7xVLX|ZI$x}f%R)30j0g7%ht6BQEkf>!_>BAOK8-!$hwFgG>647+Z>DS zve_JqZ1#oV1bI*?(gp1dX?&YpOEyw~ZJXTN<*FlO@0KzkA-atw!GI*#KtxKx-*Ak*nL$*$wxUZOzdlfrV_dFgDMc0Xp<+f&L(<&DQA_>nyOa zSUWA&nR<(L1`bx+oMBd|WtY7Jt=ENIX(Oz}MxedmqRoIUf#6lcHLSq8{v=w9KAClTc^clYW zCypOtBmbH*nWva7u&E}?6q9+f$vg>0ge=f)Ch68D%R3*1pJQD}wTJ#?fLg#x-GKZN z8C94)v#J6GnmWliX}odL1ml#6rYVzfYMMHkNMSH#A~+aY>Hi@Z{!wQ%n%Yg~36KV6 zG)*v?#v4uJVDb#;b(mW^hV_-jozpph@=J)RC!vuv)JNV(8d2mB;qL!7qJhB2*%(IX9`M(9U0 z>qoX=U}zmlK}U~b3}fg}y{v1Z|L0=((T3Feqo3+s)fR~G{L*Bp-$IWuWxG5H#cGi^AezFad*l1^7k^Y)UizVvs_%J9PKTN=qVoHfPwgxvYDaK^zf?CYO`8hQ3BkErq$)O+MJ-x2!^zf$_V+awlL&R3;(cEk*pS0&=w2Y zLQcqKgiKmUCxvVpV{JZ%$tnv8i&Pk5norXIjxhXyt`LM2f=GZ%(53|;$qNZyh-PcFCi2f zKFaekCxYO>^HB{S(cnd~HNg3OWC1@H3}`|@O*o_p2QlD60nBhgKNpY};40=LVfZDE z@u&o-s#LHLv4&r(C^*i?X##eQCWr&4@zc4^!@6CJ)4|wnjKj`49jwbq=5*2|tUBp- zu^u<;^?XT5f^QYE^Lbg93sHt&58?Hqon@U?mUZF4vJM7jv8gQ(sjMZA@fS@42fEFEO23Uaz!*8~%N~K24nAK{NY(}+4R2lRtomMIEN{&-7 z^s-i=Rx4Om$!nBaL8aGG7=%P5vq=J!M8-fbAnyn0-JS+s@zxkGk;(9q6>jjV^UL?r rc+ILLryMzz0JT!1A^-Sy4i&xod;p+-E58-*_8!JjzL8*j3FH3-{0BfI From c4b748ad2d42388279a059dbb9ba1110346753fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 1 Jun 2021 14:17:29 +0200 Subject: [PATCH 43/93] Shaders: add a generic texture layer attribute and UBO definition. The attribute will be used for instanced texture layers in texture-array-enabled shaders. --- src/Magnum/Shaders/Generic.h | 27 +++++++++++++++++------- src/Magnum/Shaders/GenericGL.h | 28 ++++++++++++++++++++----- src/Magnum/Shaders/Test/GenericTest.cpp | 10 ++++++++- 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/src/Magnum/Shaders/Generic.h b/src/Magnum/Shaders/Generic.h index 9322b1054..e15c60def 100644 --- a/src/Magnum/Shaders/Generic.h +++ b/src/Magnum/Shaders/Generic.h @@ -374,10 +374,9 @@ Used only if @ref DistanceFieldVectorGL::Flag::TextureTransformation, */ struct TextureTransformationUniform { /** @brief Construct with default parameters */ - constexpr explicit TextureTransformationUniform(DefaultInitT = DefaultInit) noexcept: rotationScaling{Math::IdentityInit} + constexpr explicit TextureTransformationUniform(DefaultInitT = DefaultInit) noexcept: rotationScaling{Math::IdentityInit}, offset{0.0f, 0.0f}, layer{0} #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) - /* Otherwise it refuses to constexpr, on 3.8 at least */ - , _pad0{}, _pad1{} + , _pad0{} /* Otherwise it refuses to constexpr, on 3.8 at least */ #endif {} /** @brief Construct without initializing the contents */ @@ -407,6 +406,15 @@ struct TextureTransformationUniform { return *this; } + /** + * @brief Set the @ref layer field + * @return Reference to self (for method chaining) + */ + TextureTransformationUniform& setLayer(UnsignedInt layer) { + this->layer = layer; + return *this; + } + /** * @} */ @@ -449,6 +457,14 @@ struct TextureTransformationUniform { */ Vector2 offset; + /** + * @brief Texture layer + * + * Descibes which layer of a texture array to use. Default value is + * @cpp 0.5f @ce. + */ + UnsignedInt layer; + /* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK I MADE THOSE UNNAMED, YOU DUMB FOOL */ #ifndef DOXYGEN_GENERATING_OUTPUT @@ -456,11 +472,6 @@ struct TextureTransformationUniform { #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) _pad0 /* Otherwise it refuses to constexpr, on 3.8 at least */ #endif - :32; /* reserved for layer */ - Int - #if (defined(CORRADE_TARGET_CLANG) && __clang_major__ < 4) || (defined(CORRADE_TARGET_APPLE_CLANG) && __clang_major__ < 8) - _pad1 /* Otherwise it refuses to constexpr, on 3.8 at least */ - #endif :32; /* reserved for coordinateSet */ #endif }; diff --git a/src/Magnum/Shaders/GenericGL.h b/src/Magnum/Shaders/GenericGL.h index d40318ae3..eb748f994 100644 --- a/src/Magnum/Shaders/GenericGL.h +++ b/src/Magnum/Shaders/GenericGL.h @@ -186,9 +186,7 @@ rotation and scale 15 -@ref TextureOffset (instanced) - -* *Reserved* --- third component for a layer +@ref TextureOffset / @ref TextureOffsetLayer (instanced) * *Reserved* --- a single component \n @@ -420,8 +418,9 @@ template struct GenericGL { * @brief (Instanced) texture offset * @m_since{2020,06} * - * @ref Magnum::Vector2 "Vector2". Currently doesn't have a corresponding - * @ref Trade::MeshAttribute. + * @ref Magnum::Vector2 "Vector2". Use either this or the + * @ref TextureOffsetLayer attribute. Currently doesn't have a + * corresponding @ref Trade::MeshAttribute. * @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} * @requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays}, * @gl_extension{EXT,instanced_arrays} or @@ -430,6 +429,22 @@ template struct GenericGL { * in WebGL 1.0. */ typedef GL::Attribute<15, Vector2> TextureOffset; + + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief (Instanced) texture offset and layer + * @m_since_latest + * + * @ref Magnum::Vector3 "Vector3", with the last component interpreted as + * an integer. Use either this or the @ref TextureOffset attribute. + * Currently doesn't have a corresponding @ref Trade::MeshAttribute. + * @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 GL::Attribute<15, Vector3> TextureOffsetLayer; + #endif }; #endif @@ -462,6 +477,9 @@ struct BaseGenericGL { #endif typedef GL::Attribute<15, Vector2> TextureOffset; + #ifndef MAGNUM_TARGET_GLES2 + typedef GL::Attribute<15, Vector3> TextureOffsetLayer; + #endif }; template<> struct GenericGL<2>: BaseGenericGL { diff --git a/src/Magnum/Shaders/Test/GenericTest.cpp b/src/Magnum/Shaders/Test/GenericTest.cpp index 0abd1809c..cf0782601 100644 --- a/src/Magnum/Shaders/Test/GenericTest.cpp +++ b/src/Magnum/Shaders/Test/GenericTest.cpp @@ -378,6 +378,8 @@ void GenericTest::textureTransformationUniformConstructDefault() { })); CORRADE_COMPARE(a.offset, (Vector2{0.0f, 0.0f})); CORRADE_COMPARE(b.offset, (Vector2{0.0f, 0.0f})); + CORRADE_COMPARE(a.layer, 0); + CORRADE_COMPARE(b.layer, 0); constexpr TextureTransformationUniform ca; constexpr TextureTransformationUniform cb{DefaultInit}; @@ -391,6 +393,8 @@ void GenericTest::textureTransformationUniformConstructDefault() { })); CORRADE_COMPARE(ca.offset, (Vector2{0.0f, 0.0f})); CORRADE_COMPARE(cb.offset, (Vector2{0.0f, 0.0f})); + CORRADE_COMPARE(ca.layer, 0); + CORRADE_COMPARE(cb.layer, 0); CORRADE_VERIFY(std::is_nothrow_default_constructible::value); CORRADE_VERIFY(std::is_nothrow_constructible::value); @@ -403,6 +407,7 @@ void GenericTest::textureTransformationUniformConstructNoInit() { TextureTransformationUniform a; a.rotationScaling[1] = {2.5f, -3.0f}; a.offset = {2.7f, 0.3f}; + a.layer = 37; new(&a) TextureTransformationUniform{NoInit}; { @@ -411,6 +416,7 @@ void GenericTest::textureTransformationUniformConstructNoInit() { #endif CORRADE_COMPARE(a.rotationScaling[1], (Vector2{2.5f, -3.0f})); CORRADE_COMPARE(a.offset, (Vector2{2.7f, 0.3f})); + CORRADE_COMPARE(a.layer, 37); } CORRADE_VERIFY(std::is_nothrow_constructible::value); @@ -422,12 +428,14 @@ void GenericTest::textureTransformationUniformConstructNoInit() { void GenericTest::textureTransformationUniformSetters() { TextureTransformationUniform a; a.setTextureMatrix(Matrix3::translation({2.6f, 0.3f})* - Matrix3::rotation(90.0_degf)); + Matrix3::rotation(90.0_degf)) + .setLayer(37); CORRADE_COMPARE(a.rotationScaling, (Matrix2x2{ Vector2{ 0.0f, 1.0f}, Vector2{-1.0f, 0.0f} })); CORRADE_COMPARE(a.offset, (Vector2{2.6f, 0.3f})); + CORRADE_COMPARE(a.layer, 37); } }}}} From 658e8109a171fc060bb836f44772e16a9cdd59bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 2 Jun 2021 11:26:23 +0200 Subject: [PATCH 44/93] Shaders: fix grouping in Phong.vert. --- src/Magnum/Shaders/Phong.vert | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Magnum/Shaders/Phong.vert b/src/Magnum/Shaders/Phong.vert index 97f5920ba..318a28480 100644 --- a/src/Magnum/Shaders/Phong.vert +++ b/src/Magnum/Shaders/Phong.vert @@ -224,8 +224,6 @@ in mediump vec3 bitangent; layout(location = TEXTURECOORDINATES_ATTRIBUTE_LOCATION) #endif in mediump vec2 textureCoordinates; - -out mediump vec2 interpolatedTextureCoordinates; #endif #ifdef VERTEX_COLOR @@ -233,8 +231,6 @@ out mediump vec2 interpolatedTextureCoordinates; layout(location = COLOR_ATTRIBUTE_LOCATION) #endif in lowp vec4 vertexColor; - -out lowp vec4 interpolatedVertexColor; #endif #ifdef INSTANCED_OBJECT_ID @@ -242,8 +238,6 @@ out lowp vec4 interpolatedVertexColor; layout(location = OBJECT_ID_ATTRIBUTE_LOCATION) #endif in highp uint instanceObjectId; - -flat out highp uint interpolatedInstanceObjectId; #endif #ifdef INSTANCED_TRANSFORMATION @@ -267,6 +261,18 @@ in mediump vec2 instancedTextureOffset; /* Outputs */ +#ifdef TEXTURED +out mediump vec2 interpolatedTextureCoordinates; +#endif + +#ifdef VERTEX_COLOR +out lowp vec4 interpolatedVertexColor; +#endif + +#ifdef INSTANCED_OBJECT_ID +flat out highp uint interpolatedInstanceObjectId; +#endif + #if LIGHT_COUNT out mediump vec3 transformedNormal; #ifdef NORMAL_TEXTURE From a95fa9f3ded0eff83075f1d1ea9626c713e3ed43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 2 Jun 2021 13:42:36 +0200 Subject: [PATCH 45/93] Shaders: what's up with the method order here. --- src/Magnum/Shaders/PhongGL.h | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index 1e0f743e3..a16d5be33 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -1330,6 +1330,17 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @name Texture binding */ + /** + * @brief Bind an ambient texture + * @return Reference to self (for method chaining) + * + * Expects that the shader was created with @ref Flag::AmbientTexture + * enabled. + * @see @ref bindTextures(), @ref setAmbientColor(), + * @ref Shaders-PhongGL-lights-ambient + */ + PhongGL& bindAmbientTexture(GL::Texture2D& texture); + /** * @brief Bind a diffuse texture * @return Reference to self (for method chaining) @@ -1342,15 +1353,15 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { PhongGL& bindDiffuseTexture(GL::Texture2D& texture); /** - * @brief Bind an ambient texture + * @brief Bind a specular texture * @return Reference to self (for method chaining) * - * Expects that the shader was created with @ref Flag::AmbientTexture - * enabled. - * @see @ref bindTextures(), @ref setAmbientColor(), - * @ref Shaders-PhongGL-lights-ambient + * 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. + * @see @ref bindTextures(), @ref setSpecularColor() */ - PhongGL& bindAmbientTexture(GL::Texture2D& texture); + PhongGL& bindSpecularTexture(GL::Texture2D& texture); /** * @brief Bind a normal texture @@ -1366,17 +1377,6 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { */ PhongGL& bindNormalTexture(GL::Texture2D& texture); - /** - * @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. - * @see @ref bindTextures(), @ref setSpecularColor() - */ - PhongGL& bindSpecularTexture(GL::Texture2D& texture); - /** * @brief Bind textures * @return Reference to self (for method chaining) 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 46/93] 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 From d845793b8fbd2950cc408f232dd5e0233705f9fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 3 Jun 2021 14:24:49 +0200 Subject: [PATCH 47/93] Shaders: benchmark texture array variants. --- .../Shaders/Test/ShadersGLBenchmark.cpp | 68 ++++++++++++++++--- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp index dc2cf2e5f..e9ae264d3 100644 --- a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp +++ b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp @@ -55,6 +55,7 @@ #include "Magnum/Trade/MeshData.h" #ifndef MAGNUM_TARGET_GLES2 +#include "Magnum/GL/TextureArray.h" #include "Magnum/Shaders/DistanceFieldVector.h" #include "Magnum/Shaders/Flat.h" #include "Magnum/Shaders/Generic.h" @@ -117,6 +118,9 @@ struct ShadersGLBenchmark: GL::OpenGLTester { GL::Mesh _mesh, _meshInstanced, _meshDuplicated; GL::Texture2D _textureWhite, _textureBlue; + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2DArray _textureWhiteArray, _textureBlueArray; + #endif }; using namespace Math::Literals; @@ -138,6 +142,9 @@ const struct { {"object ID", FlatGL2D::Flag::ObjectId, 1, 1}, #endif {"textured", FlatGL2D::Flag::Textured, 1, 1}, + #ifndef MAGNUM_TARGET_GLES2 + {"texture array", FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays, 1, 1}, + #endif {"textured + alpha mask", FlatGL2D::Flag::Textured|FlatGL2D::Flag::AlphaMask, 1, 1}, {"texture transformation", FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1, 1}, {"instanced transformation", FlatGL2D::Flag::InstancedTransformation, 1, 1}, @@ -149,6 +156,7 @@ const struct { #ifndef MAGNUM_TARGET_GLES2 {"UBO single", FlatGL2D::Flag::UniformBuffers, 1, 1}, {"UBO single, texture transformation", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureTransformation, 1, 1}, + {"UBO single, texture array transformation", FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::Textured|FlatGL2D::Flag::TextureArrays|FlatGL2D::Flag::TextureTransformation, 1, 1}, {"UBO multi", FlatGL2D::Flag::UniformBuffers, 32, 128}, {"multidraw", FlatGL2D::Flag::MultiDraw, 32, 128}, #endif @@ -168,6 +176,9 @@ const struct { #endif {"diffuse texture", PhongGL::Flag::DiffuseTexture, 1, 1, 1}, {"ADS textures", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture, 1, 1, 1}, + #ifndef MAGNUM_TARGET_GLES2 + {"ADS texture arrays", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureArrays, 1, 1, 1}, + #endif {"ADS textures + alpha mask", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::AlphaMask, 1, 1, 1}, {"ADS textures + transformation", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureTransformation, 1, 1, 1}, {"normal texture", PhongGL::Flag::NormalTexture, 1, 1, 1}, @@ -183,6 +194,7 @@ const struct { {"UBO single, zero lights", PhongGL::Flag::UniformBuffers, 0, 1, 1}, {"UBO single five lights", PhongGL::Flag::UniformBuffers, 5, 1, 1}, {"UBO single, ADS textures + transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureTransformation, 1, 1, 1}, + {"UBO single, ADS texture arrays + transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::TextureTransformation, 1, 1, 1}, {"UBO multi, one light", PhongGL::Flag::UniformBuffers, 1, 32, 128}, {"multidraw, one light", PhongGL::Flag::MultiDraw, 1, 32, 128}, #endif @@ -452,6 +464,13 @@ ShadersGLBenchmark::ShadersGLBenchmark(): _framebuffer{{{}, RenderSize}} { #endif , {1, 1}) .setSubImage(0, {}, ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, white}); + #ifndef MAGNUM_TARGET_GLES2 + _textureWhiteArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::RGBA8, {1, 1, 1}) + .setSubImage(0, {}, ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, white}); + #endif } { const Color4ub blue[1] { 0x0000ffff_rgba }; _textureBlue.setMinificationFilter(GL::SamplerFilter::Linear) @@ -465,6 +484,13 @@ ShadersGLBenchmark::ShadersGLBenchmark(): _framebuffer{{{}, RenderSize}} { #endif , {1, 1}) .setSubImage(0, {}, ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, blue}); + #ifndef MAGNUM_TARGET_GLES2 + _textureBlueArray.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::RGBA8, {1, 1, 1}) + .setSubImage(0, {}, ImageView2D{PixelFormat::RGBA8Unorm, {1, 1}, blue}); + #endif } /* Load the plugins directly from the build tree. Otherwise they're either @@ -569,8 +595,16 @@ template void ShadersGLBenchmark::flat() { if(data.flags >= FlatGL2D::Flag::AlphaMask) shader.setAlphaMask(0.0f); } - if(data.flags >= FlatGL2D::Flag::Textured) - shader.bindTexture(_textureWhite); + if(data.flags >= FlatGL2D::Flag::Textured) { + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & FlatGL2D::Flag::TextureArrays) { + shader.bindTexture(_textureWhiteArray); + } else + #endif + { + shader.bindTexture(_textureWhite); + } + } GL::Mesh* mesh; /* InstancedTextureOffset is a superset of TextureTransformation, so @@ -685,14 +719,28 @@ void ShadersGLBenchmark::phong() { if(data.flags >= PhongGL::Flag::AlphaMask) shader.setAlphaMask(0.0f); } - if(data.flags >= PhongGL::Flag::AmbientTexture) - shader.bindAmbientTexture(_textureWhite); - if(data.flags >= PhongGL::Flag::DiffuseTexture) - shader.bindDiffuseTexture(_textureWhite); - if(data.flags >= PhongGL::Flag::SpecularTexture) - shader.bindSpecularTexture(_textureWhite); - if(data.flags >= PhongGL::Flag::NormalTexture) - shader.bindNormalTexture(_textureBlue); + #ifndef MAGNUM_TARGET_GLES2 + if(data.flags & PhongGL::Flag::TextureArrays) { + if(data.flags >= PhongGL::Flag::AmbientTexture) + shader.bindAmbientTexture(_textureWhiteArray); + if(data.flags >= PhongGL::Flag::DiffuseTexture) + shader.bindDiffuseTexture(_textureWhiteArray); + if(data.flags >= PhongGL::Flag::SpecularTexture) + shader.bindSpecularTexture(_textureWhiteArray); + if(data.flags >= PhongGL::Flag::NormalTexture) + shader.bindNormalTexture(_textureBlueArray); + } else + #endif + { + if(data.flags >= PhongGL::Flag::AmbientTexture) + shader.bindAmbientTexture(_textureWhite); + if(data.flags >= PhongGL::Flag::DiffuseTexture) + shader.bindDiffuseTexture(_textureWhite); + if(data.flags >= PhongGL::Flag::SpecularTexture) + shader.bindSpecularTexture(_textureWhite); + if(data.flags >= PhongGL::Flag::NormalTexture) + shader.bindNormalTexture(_textureBlue); + } GL::Mesh* mesh; /* InstancedTextureOffset is a superset of TextureTransformation, so From 99966194cc754a3107bc1bf133f74042aa7fadf1 Mon Sep 17 00:00:00 2001 From: jvn Date: Sat, 5 Jun 2021 12:35:14 +0200 Subject: [PATCH 48/93] MeshTools: update a broken link. --- src/Magnum/MeshTools/Tipsify.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magnum/MeshTools/Tipsify.h b/src/Magnum/MeshTools/Tipsify.h index 8e589dc8c..bfb3a93fe 100644 --- a/src/Magnum/MeshTools/Tipsify.h +++ b/src/Magnum/MeshTools/Tipsify.h @@ -53,7 +53,7 @@ Optimizes the mesh for vertex-bound applications by rearranging its index array for beter usage of post-transform vertex cache. Algorithm used: * *Pedro V. Sander, Diego Nehab, and Joshua Barczak --- Fast Triangle Reordering for Vertex Locality and Reduced Overdraw, SIGGRAPH 2007, -http://gfx.cs.princeton.edu/pubs/Sander_2007_%3ETR/index.php*. +https://gfx.cs.princeton.edu/pubs/Sander_2007_%3eTR/tipsy.pdf*. @todo Ability to compute vertex count automatically */ MAGNUM_MESHTOOLS_EXPORT void tipsifyInPlace(const Containers::StridedArrayView1D& indices, UnsignedInt vertexCount, std::size_t cacheSize); From c785ef2e4a631a8ca38a471d0b5c3bac3deb47bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 6 Jun 2021 16:41:29 +0200 Subject: [PATCH 49/93] Forgot to update the year here. --- COPYING | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/COPYING b/COPYING index 4af80acfd..a5e6fdb35 100644 --- a/COPYING +++ b/COPYING @@ -1,5 +1,5 @@ Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, - 2020 Vladimír Vondruš and contributors + 2020, 2021 Vladimír Vondruš and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), From 007360a9c8891ea2396f327d3cf7490eefb921bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 6 Jun 2021 18:40:07 +0200 Subject: [PATCH 50/93] GL,Shaders: allow draw() and friends to be chained. It had a point originally, but with the new multidraw workflows it's just annoying. --- doc/changelog.dox | 9 +++++ doc/snippets/MagnumGL.cpp | 25 +++++++++++- src/Magnum/GL/AbstractShaderProgram.cpp | 40 +++++++++++--------- src/Magnum/GL/AbstractShaderProgram.h | 43 +++++++++++++-------- src/Magnum/Shaders/DistanceFieldVectorGL.h | 22 +++++++++++ src/Magnum/Shaders/FlatGL.h | 22 +++++++++++ src/Magnum/Shaders/MeshVisualizerGL.h | 44 ++++++++++++++++++++++ src/Magnum/Shaders/PhongGL.h | 22 +++++++++++ src/Magnum/Shaders/VectorGL.h | 22 +++++++++++ src/Magnum/Shaders/VertexColorGL.h | 22 +++++++++++ 10 files changed, 236 insertions(+), 35 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index b0b55502b..2cb5cfe4b 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -228,6 +228,15 @@ See also: @subsubsection changelog-latest-changes-gl GL library +- @ref GL::AbstractShaderProgram::draw(), + @relativeref{GL::AbstractShaderProgram,drawTransformFeedback()} and + @relativeref{GL::AbstractShaderProgram,dispatchCompute()} APIs now return + a reference to self and all subclasses in @ref Shaders return a subclass + reference from these. The functions used to @cpp void @ce as that made more + sense in the classic workflow where a large set of uniforms had to be set + prior to every draw, however with the new multidraw workflows that's no + longer the case and the inability to chain draw calls proved to be + annoying. - The @ref GL::Context class got significantly optimized in terms of compile time, header size and runtime as well, significantly reducing the amount of allocations done at startup. diff --git a/doc/snippets/MagnumGL.cpp b/doc/snippets/MagnumGL.cpp index a17f87d98..1a4baa4ea 100644 --- a/doc/snippets/MagnumGL.cpp +++ b/doc/snippets/MagnumGL.cpp @@ -269,11 +269,32 @@ enum: UnsignedInt { /* [AbstractShaderProgram-output-attributes] */ #if !defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) -/* [AbstractShaderProgram-hide-irrelevant] */ +/* [AbstractShaderProgram-return-hide-irrelevant] */ +public: + MyShader& draw(GL::Mesh& mesh) { + return static_cast(GL::AbstractShaderProgram::draw(mesh)); + } + MyShader& draw(GL::Mesh&& mesh) { + return static_cast(GL::AbstractShaderProgram::draw(mesh)); + } + MyShader& draw(GL::MeshView& mesh) { + return static_cast(GL::AbstractShaderProgram::draw(mesh)); + } + MyShader& draw(GL::MeshView&& mesh) { + return static_cast(GL::AbstractShaderProgram::draw(mesh)); + } + /* Omit these if the shader is not ready for multidraw */ + MyShader& draw(Containers::ArrayView> meshes) { + return static_cast(GL::AbstractShaderProgram::draw(meshes)); + } + MyShader& draw(std::initializer_list> meshes) { + return static_cast(GL::AbstractShaderProgram::draw(meshes)); + } + private: using GL::AbstractShaderProgram::drawTransformFeedback; using GL::AbstractShaderProgram::dispatchCompute; -/* [AbstractShaderProgram-hide-irrelevant] */ +/* [AbstractShaderProgram-return-hide-irrelevant] */ public: #endif diff --git a/src/Magnum/GL/AbstractShaderProgram.cpp b/src/Magnum/GL/AbstractShaderProgram.cpp index 05685f68e..06603814a 100644 --- a/src/Magnum/GL/AbstractShaderProgram.cpp +++ b/src/Magnum/GL/AbstractShaderProgram.cpp @@ -350,11 +350,11 @@ std::pair AbstractShaderProgram::validate() { return {success, std::move(message)}; } -void AbstractShaderProgram::draw(Mesh& mesh) { - CORRADE_ASSERT(mesh._countSet, "GL::AbstractShaderProgram::draw(): Mesh::setCount() was never called, probably a mistake?", ); +AbstractShaderProgram& AbstractShaderProgram::draw(Mesh& mesh) { + CORRADE_ASSERT(mesh._countSet, "GL::AbstractShaderProgram::draw(): Mesh::setCount() was never called, probably a mistake?", *this); /* Nothing to draw, exit without touching any state */ - if(!mesh._count || !mesh._instanceCount) return; + if(!mesh._count || !mesh._instanceCount) return *this; use(); @@ -363,13 +363,14 @@ void AbstractShaderProgram::draw(Mesh& mesh) { #else mesh.drawInternal(mesh._count, mesh._baseVertex, mesh._instanceCount, mesh._indexOffset); #endif + return *this; } -void AbstractShaderProgram::draw(MeshView& mesh) { - CORRADE_ASSERT(mesh._countSet, "GL::AbstractShaderProgram::draw(): MeshView::setCount() was never called, probably a mistake?", ); +AbstractShaderProgram& AbstractShaderProgram::draw(MeshView& mesh) { + CORRADE_ASSERT(mesh._countSet, "GL::AbstractShaderProgram::draw(): MeshView::setCount() was never called, probably a mistake?", *this); /* Nothing to draw, exit without touching any state */ - if(!mesh._count || !mesh._instanceCount) return; + if(!mesh._count || !mesh._instanceCount) return *this; use(); @@ -378,17 +379,18 @@ void AbstractShaderProgram::draw(MeshView& mesh) { #else mesh._original->drawInternal(mesh._count, mesh._baseVertex, mesh._instanceCount, mesh._indexOffset); #endif + return *this; } -void AbstractShaderProgram::draw(Containers::ArrayView> meshes) { - if(meshes.empty()) return; +AbstractShaderProgram& AbstractShaderProgram::draw(Containers::ArrayView> meshes) { + if(meshes.empty()) return *this; use(); #ifndef CORRADE_NO_ASSERT const Mesh* original = &*meshes.front()->_original; for(std::size_t i = 0; i != meshes.size(); ++i) - CORRADE_ASSERT(&*meshes[i]->_original == original, "GL::AbstractShaderProgram::draw(): all meshes must be views of the same original mesh, expected" << original << "but got" << &*meshes[i]->_original << "at index" << i, ); + CORRADE_ASSERT(&*meshes[i]->_original == original, "GL::AbstractShaderProgram::draw(): all meshes must be views of the same original mesh, expected" << original << "but got" << &*meshes[i]->_original << "at index" << i, *this); #endif #ifndef MAGNUM_TARGET_GLES @@ -396,34 +398,38 @@ void AbstractShaderProgram::draw(Containers::ArrayView> meshes) { - draw(Containers::arrayView(meshes)); +AbstractShaderProgram& AbstractShaderProgram::draw(std::initializer_list> meshes) { + return draw(Containers::arrayView(meshes)); } #ifndef MAGNUM_TARGET_GLES -void AbstractShaderProgram::drawTransformFeedback(Mesh& mesh, TransformFeedback& xfb, UnsignedInt stream) { +AbstractShaderProgram& AbstractShaderProgram::drawTransformFeedback(Mesh& mesh, TransformFeedback& xfb, UnsignedInt stream) { /* Nothing to draw, exit without touching any state */ - if(!mesh._instanceCount) return; + if(!mesh._instanceCount) return *this; use(); mesh.drawInternal(xfb, stream, mesh._instanceCount); + return *this; } -void AbstractShaderProgram::drawTransformFeedback(MeshView& mesh, TransformFeedback& xfb, UnsignedInt stream) { - /* Nothing to draw, exit without touching any state */ - if(!mesh._instanceCount) return; +AbstractShaderProgram& AbstractShaderProgram::drawTransformFeedback(MeshView& mesh, TransformFeedback& xfb, UnsignedInt stream) { + /* If nothing to draw, exit without touching any state */ + if(mesh._instanceCount) return *this; use(); mesh._original->drawInternal(xfb, stream, mesh._instanceCount); + return *this; } #endif #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) -void AbstractShaderProgram::dispatchCompute(const Vector3ui& workgroupCount) { +AbstractShaderProgram& AbstractShaderProgram::dispatchCompute(const Vector3ui& workgroupCount) { use(); glDispatchCompute(workgroupCount.x(), workgroupCount.y(), workgroupCount.z()); + return *this; } #endif diff --git a/src/Magnum/GL/AbstractShaderProgram.h b/src/Magnum/GL/AbstractShaderProgram.h index fe54fae93..101dc46d2 100644 --- a/src/Magnum/GL/AbstractShaderProgram.h +++ b/src/Magnum/GL/AbstractShaderProgram.h @@ -87,12 +87,14 @@ functions and properties: @snippet MagnumGL.cpp AbstractShaderProgram-xfb -

  • And optionally, **hiding irrelevant draw/dispatch functions** to prevent - users from accidentally calling @ref draw() on compute shaders, - @ref drawTransformFeedback() on shaders that don't have transform feedback - or @ref dispatchCompute() on shaders that aren't compute. For example: - - @snippet MagnumGL.cpp AbstractShaderProgram-hide-irrelevant +
  • And optionally, **return derived type from relevant draw/dispatch functions** + to make it possible for users to easily chain draw calls, and on the other + hand **hide the irrelevant APIs** to prevent users from accidentally + calling @ref draw() on compute shaders, @ref drawTransformFeedback() on + shaders that don't have transform feedback or @ref dispatchCompute() on + shaders that aren't compute. For example: + + @snippet MagnumGL.cpp AbstractShaderProgram-return-hide-irrelevant @subsection GL-AbstractShaderProgram-attribute-location Binding attribute and fragment data location @@ -751,7 +753,7 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { /** * @brief Draw a mesh - * @param mesh Mesh to draw + * @return Reference to self (for method chaining) * @m_since{2020,06} * * Expects that @p mesh is compatible with this shader and is fully set @@ -794,16 +796,19 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * @requires_gl Specifying base vertex for indexed meshes is not * available in OpenGL ES or WebGL. */ - void draw(Mesh& mesh); + AbstractShaderProgram& draw(Mesh& mesh); /** * @overload * @m_since{2020,06} */ - void draw(Mesh&& mesh) { draw(mesh); } + AbstractShaderProgram& draw(Mesh&& mesh) { + return draw(mesh); + } /** * @brief Draw a mesh view + * @return Reference to self (for method chaining) * @m_since{2020,06} * * See @ref draw(Mesh&) for more information. @@ -828,16 +833,19 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * @requires_gl Specifying base vertex for indexed meshes is not * available in OpenGL ES or WebGL. */ - void draw(MeshView& mesh); + AbstractShaderProgram& draw(MeshView& mesh); /** * @overload * @m_since{2020,06} */ - void draw(MeshView&& mesh) { draw(mesh); } + AbstractShaderProgram& draw(MeshView&& mesh) { + return draw(mesh); + } /** * @brief Draw multiple meshes at once + * @return Reference to self (for method chaining) * @m_since{2020,06} * * On OpenGL ES, if neither @gl_extension{EXT,multi_draw_arrays} nor @@ -872,13 +880,13 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * if the mesh is indexed and @ref MeshView::baseVertex() is not * `0` */ - void draw(Containers::ArrayView> meshes); + AbstractShaderProgram& draw(Containers::ArrayView> meshes); /** * @overload * @m_since{2020,06} */ - void draw(std::initializer_list> meshes); + AbstractShaderProgram& draw(std::initializer_list> meshes); #ifndef MAGNUM_TARGET_GLES /** @@ -886,6 +894,7 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * @param mesh Mesh to draw * @param xfb Transform feedback to use for vertex count * @param stream Transform feedback stream ID + * @return Reference to self (for method chaining) * @m_since{2020,06} * * Expects that @p mesh is compatible with this shader, is fully set up @@ -911,10 +920,11 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * @requires_gl42 Extension @gl_extension{ARB,transform_feedback_instanced} * if @ref Mesh::instanceCount() is more than `1` */ - void drawTransformFeedback(Mesh& mesh, TransformFeedback& xfb, UnsignedInt stream = 0); + AbstractShaderProgram& drawTransformFeedback(Mesh& mesh, TransformFeedback& xfb, UnsignedInt stream = 0); /** * @brief Draw a mesh view with vertices coming out of transform feedback + * @return Reference to self (for method chaining) * @m_since{2020,06} * * Everything set by @ref MeshView::setCount(), @@ -931,13 +941,14 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * @requires_gl42 Extension @gl_extension{ARB,transform_feedback_instanced} * if @ref MeshView::instanceCount() is more than `1` */ - void drawTransformFeedback(MeshView& mesh, TransformFeedback& xfb, UnsignedInt stream = 0); + AbstractShaderProgram& drawTransformFeedback(MeshView& mesh, TransformFeedback& xfb, UnsignedInt stream = 0); #endif #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) /** * @brief Dispatch compute * @param workgroupCount Workgroup count in given dimension + * @return Reference to self (for method chaining) * * Valid only on programs with compute shader attached. * @see @fn_gl{DispatchCompute} @@ -946,7 +957,7 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * and older. * @requires_gles Compute shaders are not available in WebGL. */ - void dispatchCompute(const Vector3ui& workgroupCount); + AbstractShaderProgram& dispatchCompute(const Vector3ui& workgroupCount); #endif protected: diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.h b/src/Magnum/Shaders/DistanceFieldVectorGL.h index ffd3a40fe..abd2e926c 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.h +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.h @@ -531,6 +531,28 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * @} */ + /* Overloads to remove WTF-factor from method chaining order */ + #ifndef DOXYGEN_GENERATING_OUTPUT + DistanceFieldVectorGL& draw(GL::Mesh& mesh) { + return static_cast&>(GL::AbstractShaderProgram::draw(mesh)); + } + DistanceFieldVectorGL& draw(GL::Mesh&& mesh) { + return static_cast&>(GL::AbstractShaderProgram::draw(mesh)); + } + DistanceFieldVectorGL& draw(GL::MeshView& mesh) { + return static_cast&>(GL::AbstractShaderProgram::draw(mesh)); + } + DistanceFieldVectorGL& draw(GL::MeshView&& mesh) { + return static_cast&>(GL::AbstractShaderProgram::draw(mesh)); + } + DistanceFieldVectorGL& draw(Containers::ArrayView> meshes) { + return static_cast&>(GL::AbstractShaderProgram::draw(meshes)); + } + DistanceFieldVectorGL& draw(std::initializer_list> meshes) { + return static_cast&>(GL::AbstractShaderProgram::draw(meshes)); + } + #endif + private: /* Prevent accidentally calling irrelevant functions */ #ifndef MAGNUM_TARGET_GLES diff --git a/src/Magnum/Shaders/FlatGL.h b/src/Magnum/Shaders/FlatGL.h index 5230d5d8d..d57785615 100644 --- a/src/Magnum/Shaders/FlatGL.h +++ b/src/Magnum/Shaders/FlatGL.h @@ -860,6 +860,28 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * @} */ + /* Overloads to remove WTF-factor from method chaining order */ + #ifndef DOXYGEN_GENERATING_OUTPUT + FlatGL& draw(GL::Mesh& mesh) { + return static_cast&>(GL::AbstractShaderProgram::draw(mesh)); + } + FlatGL& draw(GL::Mesh&& mesh) { + return static_cast&>(GL::AbstractShaderProgram::draw(mesh)); + } + FlatGL& draw(GL::MeshView& mesh) { + return static_cast&>(GL::AbstractShaderProgram::draw(mesh)); + } + FlatGL& draw(GL::MeshView&& mesh) { + return static_cast&>(GL::AbstractShaderProgram::draw(mesh)); + } + FlatGL& draw(Containers::ArrayView> meshes) { + return static_cast&>(GL::AbstractShaderProgram::draw(meshes)); + } + FlatGL& draw(std::initializer_list> meshes) { + return static_cast&>(GL::AbstractShaderProgram::draw(meshes)); + } + #endif + private: /* Prevent accidentally calling irrelevant functions */ #ifndef MAGNUM_TARGET_GLES diff --git a/src/Magnum/Shaders/MeshVisualizerGL.h b/src/Magnum/Shaders/MeshVisualizerGL.h index d96773057..fccf0cd88 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.h +++ b/src/Magnum/Shaders/MeshVisualizerGL.h @@ -595,6 +595,28 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * @} */ + /* Overloads to remove WTF-factor from method chaining order */ + #ifndef DOXYGEN_GENERATING_OUTPUT + MeshVisualizerGL2D& draw(GL::Mesh& mesh) { + return static_cast(GL::AbstractShaderProgram::draw(mesh)); + } + MeshVisualizerGL2D& draw(GL::Mesh&& mesh) { + return static_cast(GL::AbstractShaderProgram::draw(mesh)); + } + MeshVisualizerGL2D& draw(GL::MeshView& mesh) { + return static_cast(GL::AbstractShaderProgram::draw(mesh)); + } + MeshVisualizerGL2D& draw(GL::MeshView&& mesh) { + return static_cast(GL::AbstractShaderProgram::draw(mesh)); + } + MeshVisualizerGL2D& draw(Containers::ArrayView> meshes) { + return static_cast(GL::AbstractShaderProgram::draw(meshes)); + } + MeshVisualizerGL2D& draw(std::initializer_list> meshes) { + return static_cast(GL::AbstractShaderProgram::draw(meshes)); + } + #endif + private: Int _transformationProjectionMatrixUniform{6}; }; @@ -1574,6 +1596,28 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * @} */ + /* Overloads to remove WTF-factor from method chaining order */ + #ifndef DOXYGEN_GENERATING_OUTPUT + MeshVisualizerGL3D& draw(GL::Mesh& mesh) { + return static_cast(GL::AbstractShaderProgram::draw(mesh)); + } + MeshVisualizerGL3D& draw(GL::Mesh&& mesh) { + return static_cast(GL::AbstractShaderProgram::draw(mesh)); + } + MeshVisualizerGL3D& draw(GL::MeshView& mesh) { + return static_cast(GL::AbstractShaderProgram::draw(mesh)); + } + MeshVisualizerGL3D& draw(GL::MeshView&& mesh) { + return static_cast(GL::AbstractShaderProgram::draw(mesh)); + } + MeshVisualizerGL3D& draw(Containers::ArrayView> meshes) { + return static_cast(GL::AbstractShaderProgram::draw(meshes)); + } + MeshVisualizerGL3D& draw(std::initializer_list> meshes) { + return static_cast(GL::AbstractShaderProgram::draw(meshes)); + } + #endif + private: Int _transformationMatrixUniform{6}, _projectionMatrixUniform{7}; diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index c04ef613e..9c34a3b2d 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -1561,6 +1561,28 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @} */ + /* Overloads to remove WTF-factor from method chaining order */ + #ifndef DOXYGEN_GENERATING_OUTPUT + PhongGL& draw(GL::Mesh& mesh) { + return static_cast(GL::AbstractShaderProgram::draw(mesh)); + } + PhongGL& draw(GL::Mesh&& mesh) { + return static_cast(GL::AbstractShaderProgram::draw(mesh)); + } + PhongGL& draw(GL::MeshView& mesh) { + return static_cast(GL::AbstractShaderProgram::draw(mesh)); + } + PhongGL& draw(GL::MeshView&& mesh) { + return static_cast(GL::AbstractShaderProgram::draw(mesh)); + } + PhongGL& draw(Containers::ArrayView> meshes) { + return static_cast(GL::AbstractShaderProgram::draw(meshes)); + } + PhongGL& draw(std::initializer_list> meshes) { + return static_cast(GL::AbstractShaderProgram::draw(meshes)); + } + #endif + private: /* Prevent accidentally calling irrelevant functions */ #ifndef MAGNUM_TARGET_GLES diff --git a/src/Magnum/Shaders/VectorGL.h b/src/Magnum/Shaders/VectorGL.h index 51068ad54..977ec4a26 100644 --- a/src/Magnum/Shaders/VectorGL.h +++ b/src/Magnum/Shaders/VectorGL.h @@ -483,6 +483,28 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * @} */ + /* Overloads to remove WTF-factor from method chaining order */ + #ifndef DOXYGEN_GENERATING_OUTPUT + VectorGL& draw(GL::Mesh& mesh) { + return static_cast&>(GL::AbstractShaderProgram::draw(mesh)); + } + VectorGL& draw(GL::Mesh&& mesh) { + return static_cast&>(GL::AbstractShaderProgram::draw(mesh)); + } + VectorGL& draw(GL::MeshView& mesh) { + return static_cast&>(GL::AbstractShaderProgram::draw(mesh)); + } + VectorGL& draw(GL::MeshView&& mesh) { + return static_cast&>(GL::AbstractShaderProgram::draw(mesh)); + } + VectorGL& draw(Containers::ArrayView> meshes) { + return static_cast&>(GL::AbstractShaderProgram::draw(meshes)); + } + VectorGL& draw(std::initializer_list> meshes) { + return static_cast&>(GL::AbstractShaderProgram::draw(meshes)); + } + #endif + private: /* Prevent accidentally calling irrelevant functions */ #ifndef MAGNUM_TARGET_GLES diff --git a/src/Magnum/Shaders/VertexColorGL.h b/src/Magnum/Shaders/VertexColorGL.h index c13e29f76..4379062cd 100644 --- a/src/Magnum/Shaders/VertexColorGL.h +++ b/src/Magnum/Shaders/VertexColorGL.h @@ -341,6 +341,28 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ */ #endif + /* Overloads to remove WTF-factor from method chaining order */ + #ifndef DOXYGEN_GENERATING_OUTPUT + VertexColorGL& draw(GL::Mesh& mesh) { + return static_cast&>(GL::AbstractShaderProgram::draw(mesh)); + } + VertexColorGL& draw(GL::Mesh&& mesh) { + return static_cast&>(GL::AbstractShaderProgram::draw(mesh)); + } + VertexColorGL& draw(GL::MeshView& mesh) { + return static_cast&>(GL::AbstractShaderProgram::draw(mesh)); + } + VertexColorGL& draw(GL::MeshView&& mesh) { + return static_cast&>(GL::AbstractShaderProgram::draw(mesh)); + } + VertexColorGL& draw(Containers::ArrayView> meshes) { + return static_cast&>(GL::AbstractShaderProgram::draw(meshes)); + } + VertexColorGL& draw(std::initializer_list> meshes) { + return static_cast&>(GL::AbstractShaderProgram::draw(meshes)); + } + #endif + private: /* Prevent accidentally calling irrelevant functions */ #ifndef MAGNUM_TARGET_GLES From bf65bf859a51a6dd30b3ca02e43da4cb8434ddd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 8 Jun 2021 10:54:24 +0200 Subject: [PATCH 51/93] GL: might make sense to say what unit are the sizes in. No, it's not 64k vec4s, but 64 kB. --- src/Magnum/GL/AbstractShaderProgram.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Magnum/GL/AbstractShaderProgram.h b/src/Magnum/GL/AbstractShaderProgram.h index 101dc46d2..1af6737eb 100644 --- a/src/Magnum/GL/AbstractShaderProgram.h +++ b/src/Magnum/GL/AbstractShaderProgram.h @@ -586,7 +586,7 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { static Int maxCombinedShaderOutputResources(); /** - * @brief Max supported shader storage block size + * @brief Max supported shader storage block size in bytes * * The result is cached, repeated queries don't result in repeated * OpenGL calls. If neither extension @gl_extension{ARB,shader_storage_buffer_object} @@ -600,7 +600,7 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { #endif /** - * @brief Max supported uniform block size + * @brief Max supported uniform block size in bytes * * The result is cached, repeated queries don't result in repeated * OpenGL calls. If extension @gl_extension{ARB,uniform_buffer_object} From 1f2eef7b6a68fd580ca14f5b2c6dd1208cdd3206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 23 May 2021 16:12:05 +0200 Subject: [PATCH 52/93] doc: document uniform buffer and multidraw support in builtin shaders. --- doc/changelog.dox | 3 +- doc/shaders.dox | 273 +++++++++++-- doc/snippets/MagnumShaders-gl.cpp | 451 ++++++++++++++++++++- src/Magnum/Shaders/DistanceFieldVectorGL.h | 33 ++ src/Magnum/Shaders/FlatGL.h | 35 ++ src/Magnum/Shaders/MeshVisualizerGL.h | 31 ++ src/Magnum/Shaders/PhongGL.h | 38 ++ src/Magnum/Shaders/VectorGL.h | 33 ++ src/Magnum/Shaders/VertexColorGL.h | 27 ++ 9 files changed, 884 insertions(+), 40 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 2cb5cfe4b..a805c3a9a 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -149,7 +149,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 + massive driver overhead reduction. The @ref shaders overview page was + updated with an introduction the new features. - @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 diff --git a/doc/shaders.dox b/doc/shaders.dox index eb867025f..f04dd56c3 100644 --- a/doc/shaders.dox +++ b/doc/shaders.dox @@ -34,8 +34,7 @@ namespace Magnum { Magnum contains a set of general-purpose shaders for easy prototyping, UI rendering and data visualization/debugging in both 2D and 3D scenes. The -following shaders are available, see documentation of each class for sample -output and example setup: +following shaders are available: - @ref Shaders::FlatGL "Shaders::FlatGL*D" --- flat shading using single color or texture @@ -48,32 +47,245 @@ output and example setup: - @ref Shaders::MeshVisualizerGL2D / @ref Shaders::MeshVisualizerGL3D --- wireframe visualization -All the builtin shaders can be used on unextended OpenGL 2.1 and OpenGL ES 2.0 -/ WebGL 1.0, but they try to use the most recent technology available to have -them as efficient as possible on every configuration. +The essential functionality of builtin shaders can be used even on unextended +OpenGL 2.1 and OpenGL ES 2.0 / WebGL 1.0, but the code will try to use the most +recent technology available to have them as efficient as possible on every +configuration. Some functionality, such as uniform buffers, texture arrays or +object ID rendering, requires newer versions or extensions, as noted in +documentation of a particular feature. @section shaders-usage Usage -Shader usage is divided into two parts: configuring vertex attributes in the -mesh and configuring the shader itself. +Shader usage is divided into two parts: describing vertex attributes in the +mesh and setting up the shader itself. -Each shader expects some set of vertex attributes, thus when adding vertex +Each shader expects some set of vertex attributes, thus when adding a vertex buffer into the mesh, you need to specify which shader attributes are on which position in the buffer. See @ref GL::Mesh::addVertexBuffer() for details and -usage examples. Example mesh configuration for @ref Shaders::PhongGL shader: +usage examples. Example mesh configuration for the @ref Shaders::PhongGL +shader: @snippet MagnumShaders-gl.cpp shaders-setup Each shader then has its own set of configuration functions. Some configuration is static, specified commonly as flags in constructor, directly affecting compiled shader code. Other configuration is specified through uniforms and -various binding points, commonly exposed through various setters. All shader -uniforms have a reasonable defaults so you are able to see at least something -when using the shader directly without any further configuration, but in most -cases you may want to specify at least the transformation/projection matrices. -Example configuration and rendering using @link Shaders::PhongGL @endlink: +various binding points, commonly exposed through various setters. For uniforms +there's two different workflows --- a classical one, where uniforms have +immediate setters, and a uniform buffer workflow, where the uniform parameters +are saved to a structure and then uploaded to a GPU buffer. Let's compare both +approaches: -@snippet MagnumShaders-gl.cpp shaders-rendering +@subsection shaders-usage-classic Using classic uniforms + +The most straightforward and portable way, working even on old OpenGL ES 2.0 +and WebGL 1.0 platforms, is using classic uniform setters. All shader uniforms +have a reasonable defaults so you are able to see at least something when using +the shader directly without any further configuration, but in most cases you +may want to specify at least the transformation/projection matrices. +Example configuration and rendering using @link Shaders::PhongGL @endlink --- +by default it's just colored and uses a single light, and we set a color of +both in addition to transformation, projection and normal matrices: + +@snippet MagnumShaders-gl.cpp shaders-classic + +@subsection shaders-usage-ubo Using uniform buffers + +Uniform buffers require GL 3.1, OpenGL ES 3.0 or WebGL 2.0 and are more verbose +to set up, but when used the right way they can result in greatly reduced +driver overhead. Uniform buffers get enabled using the +@relativeref{Shaders::PhongGL,Flag::UniformBuffers} flag that's implemented for +all builtin shaders, and after that you're not supposed to use most of the +`set*()` APIs anymore, instead you have to fill uniform structures, upload them +to @ref GL::Buffer instances and then bind those via various `bind*Buffer()` +APIs. To simplify porting, documentation of each classic uniform setter lists +the equivalent uniform buffer APIs. + +Because some parameters such as projection, material or light setup don't +change every draw, they are organized into buffers based on expected frequency +of change. This way you can fill the projection and material buffers just once +at the start, light setup only when the camera position changes and with much +less to upload for every draw. The separation is also done in a way that makes +it possible to reuse projection/transformation data among different shaders, +e.g. for a depth pre-pass. + +In the following example, projection and transformation parameters are supplied +via generic shader-independent @ref Shaders::ProjectionUniform3D and +@ref Shaders::TransformationUniform3D structures and Phong-specific parameters +then via @ref Shaders::PhongDrawUniform, @ref Shaders::PhongMaterialUniform and +@ref Shaders::PhongLightUniform structures. While the structures expose the +fields directly, the data layout may be non-trivial and it's thus recommended +to use the setters unless they prove to be a performance bottleneck: + +@snippet MagnumShaders-gl.cpp shaders-ubo + +Altogether, this results in the same output as in the classic uniform case +shown above. Similarly to the classic uniforms, default-constructed structures +have reasonable defaults to make the shader render at least something, but note +that you *have to* bind the buffer to get the defaults, without a buffer bound +you'll get a fully black mesh at best and nothing rendered at all in the worst +cases. + +@m_class{m-block m-success} + +@par Importance of buffer usage and storage flags + With uniform buffers, it's very important what kind of memory gets used for + the backing storage. In the above snippets, the (implicit) + @ref GL::BufferUsage::StaticDraw got used for simplicity, but for data that + are changed for every draw it could make sense to pick + @ref GL::BufferUsage::DynamicDraw instead. +@par + The most ideal way may be to use @gl_extension{ARB,buffer_storage} + from OpenGL 4.4, and instead of @ref GL::Buffer::setData() calling + @relativeref{GL::Buffer,setStorage()} directly with the uniform data and + with empty @ref GL::Buffer::StorageFlags, which makes the buffer immutable. + Updating such immutable buffer can be still done via + @ref GL::Buffer::copy() from another buffer, but setting or mapping the + data from the CPU side won't be possible. +@par + As with everything, be sure to profile and pick the best workflow for your + target platform --- what's best for desktop may not be the best in WebGL, + and what works with WebGL running on top GL may not be the best with WebGL + that's itself implemented using D3D or Metal. + +@subsection shaders-usage-multidraw Multidraw and reducing driver overhead + +The main advantage of uniform buffers is the ability to specify data for +multiple draws together --- after all, having to reupload three or four buffers +for every draw like shown above wouldn't be really faster or easier than +setting the uniforms directly. On the other hand, uploading everything first +and binding a different subrange each time would avoid the reupload, but since +most drivers have uniform buffer alignment requirement as high as 256 bytes +(@ref GL::Buffer::uniformOffsetAlignment()), the per-draw buffers would have to +be very sparse. + +Instead, it's possible to construct the shaders with a statically defined +draw count, fill the buffers with data for that many draws at once and then use +@relativeref{Shaders::PhongGL,setDrawOffset()} to pick concrete per-draw +parameters. Since material parameters are commonly shared among multiple draws, +the desired usage is to upload unique materials and then reference them via a +@ref Shaders::PhongDrawUniform::materialId "Shaders::*DrawUniform::materialId". +The following snippet shows drawing three different meshes, where two of them +share the same material definition. The projection and light buffer is the same +as above: + +@snippet MagnumShaders-gl.cpp shaders-multi + +While this minimizes the state changes to just a single immediate uniform being +changed between draws, it's possible to go even further by using +@ref GL::MeshView instances onto a single @ref GL::Mesh instead of several +different @ref GL::Mesh objects --- that way the attribute layout doesn't need +to be updated and it's just submitting draws with different offsets and counts. + +Finally, with mesh views and on platforms that support @gl_extension{ARB,shader_draw_parameters} from OpenGL 4.6 or the +@gl_extension{ANGLE,multi_draw} / @webgl_extension{WEBGL,multi_draw} ES and +WebGL extension, it's possible to directly submit a multi-draw command. The +shader needs to have @relativeref{Shaders::PhongGL,Flag::MultiDraw} enabled, +which will make it use the @glsl gl_DrawID @ce builtin to pick the per-draw +parameters on its own. The above snippet modified for multidraw would then look +like this, uniform upload and binding is the same as before: + +@snippet MagnumShaders-gl.cpp shaders-multidraw + + + +@m_class{m-block m-warning} + +@par Uniform buffer size limits + Note that size of a single uniform buffer that can be bound to a shader is + quite limited (@ref GL::AbstractShaderProgram::maxUniformBlockSize(), + usually just 16 or 64 kB), and another reason the parameters are separated + and deduplicated among several buffers is to maximize use of that memory. + With that you should always be able to submit at least 256 draws at once as + the biggest per-draw uniform structure used by builtin shaders has a size + of a 4x4 matrix. +@par + For larger batches the expected workflow is to still upload everything at + once but then bind and draw smaller (and properly aligned) subranges that + fit into the limit. For convenience, all uniform structures are guaranteed + to fit evenly into multiples of 768 bytes, which should be large enough for + even the strictest @ref GL::Buffer::uniformOffsetAlignment() requirements. + +@subsection shaders-usage-instancing Instancing + +@ref Shaders::FlatGL and @ref Shaders::PhongGL support instancing, which allows +them to render the same mesh several times but with different transformation +and material applied. It can be thought of as a more constrained variant of the +multidraw mentioned above, but instead of uniform buffers the per-instance +parameters are passed through instanced mesh attributes. + +No uniform buffer requirement means this feature can be used even on OpenGL ES +2.0 and WebGL 1.0 targets if corresponding instancing extensions are available. +Using attributes instead of uniform buffers also means there's no limitation on +how many instances can be drawn at once, on the other hand a mesh can have only +a certain amount of attribute bindings and thus only the basic properties can +be specified per-instance such as the transformation matrix or base color. + +The following snippet shows a setup similar to the multidraw above, except that +it's just the same sphere drawn three times in different locations and with a +different material applied. Note that the per-instance color is achieved by +using the usual vertex color attribute, only instanced: + +@snippet MagnumShaders-gl.cpp shaders-instancing + +@subsection shaders-usage-textures Using textures + +Unless the shader requires a texture to work (which is the case of +@ref Shaders::VectorGL and @ref Shaders::DistanceFieldVectorGL), by default all +shaders are just colored. Enabling a texture is done via a flag (such as +@ref Shaders::Phong::Flag::DiffuseTexture) and then the texture is bound via an +appropriate `bind*Texture()` call. In most cases the texture value is +multiplied with the corresponding color uniform. + +@snippet MagnumShaders-gl.cpp shaders-textures + +All shaders that support textures are also able to apply arbitrary +transformation to the texture coordinate attribute by enabling +@relativeref{Shaders::PhongGL,Flag::TextureTransformation} on a particular +shader. Desired transformation is then supplied via +@relativeref{Shaders::PhongGL,setTextureMatrix()} (or a +@ref Shaders::TextureTransformationUniform in case uniform buffers are used). +This can be useful for animations, when you have a larger atlas with switchable +texture variations for a single mesh, or when you have texture coordinates +quantized in some nontrivial way. + +Texture transformation is also useful in the +@ref shaders-usage-multidraw "multidraw" or +@ref shaders-usage-instancing "instancing" scenarios mentioned above, since +each draw will most likely require a different texture. There are two options: + +- Upload the textures to subrectangles of a larger @ref GL::Texture2D and + then specify @ref Shaders::TextureTransformationUniform::offset and + @relativeref{Shaders::TextureTransformationUniform,rotationScaling} for + each draw, or in case of an instanced draw supply an instanced + @relativeref{Shaders::PhongGL,TextureOffset} attribute and have a global + scale set for all instanced via + @relativeref{Shaders::PhongGL,setTextureMatrix()}. +- Enable @relativeref{Shaders::PhongGL,Flag::TextureArrays} in the shader + (not available on OpenGL ES 2.0 or WebGL 1.0), upload the textures to + slices of a @ref GL::Texture2DArray and specify + @ref Shaders::TextureTransformationUniform::layer for each draw, or in case + of an instanced draw supply a layer in an instanced + @relativeref{Shaders::PhongGL,TextureOffsetLayer} attribute. + +While with a @ref GL::Texture2D you may hit texture size limits (not to mention +you possible issues with materials that relied on a certain wrapping mode), +@ref GL::Texture2DArray is generally able to contain a lot more data, however +all slices have to be of the same size. You can also combine the two approaches +and pack differently sized textures to slices of a texture array and then set +both offset/scale and a layer per-draw. + +The following snippet shows a multi-draw setup with a different texture array +layer used by each draw. While the projection, transformation, draw material +and light buffers are the same as before, there's a new per-draw +@ref Shaders::TextureTransformationUniform buffer supplying the layer +information: + +@snippet MagnumShaders-gl.cpp shaders-texture-arrays + +While the primary use case of texture arrays is with uniform buffers and +multidraw, they work in the classic uniform workflow as well --- use +@relativeref{Shaders::PhongGL,setTextureLayer()} there instead. @section shaders-generic Generic vertex attributes and framebuffer attachments @@ -81,28 +293,29 @@ Many shaders share the same vertex attribute definitions, such as positions, normals, texture coordinates etc. It's thus possible to configure the mesh for a *generic* shader and then render it with any compatible shader. Definition of all generic attributes is available in the -@ref Shaders::GenericGL class. Configuration of the above mesh using generic -attributes could then look like this: +@ref Shaders::GenericGL class. Setup of the mesh @ref shaders-usage "shown above" +using generic attributes could then look like this: @snippet MagnumShaders-gl.cpp shaders-generic -Note that in this particular case both configurations are equivalent, because -@ref Shaders::PhongGL also uses generic vertex attribute definitions. Then you -can render the mesh using @ref Shaders::PhongGL shader like above, or use for -example @ref Shaders::FlatGL3D or even @ref Shaders::MeshVisualizerGL3D with -the same mesh reconfiguration. The unused attributes will be simply ignored. +Note that in this particular case both setups are equivalent, because +@ref Shaders::PhongGL attribute definitions are just aliases to the generic +ones. Then you can render the mesh using the @ref Shaders::PhongGL shader like +above, or use for example @ref Shaders::FlatGL3D or even +@ref Shaders::MeshVisualizerGL3D with the same mesh reconfiguration. The unused +attributes will be simply ignored. @snippet MagnumShaders-gl.cpp shaders-meshvisualizer The @ref MeshTools::compile() utility configures meshes using generic vertex -attribute definitions to make them usable with any shader. - -Besides vertex attributes, the @ref Shaders::GenericGL contains generic -definitions for framebuffer outputs as well --- in many cases a shader has just -one (color) output, but some shaders such as @ref Shaders::FlatGL or -@ref Shaders::PhongGL offer an object ID output as well. A setup equivalent to -what's done in Flat shader's @ref Shaders-FlatGL-object-id but using the -generic definitions would look like this: +attribute definitions to make them usable with any builtin shader. + +Besides vertex attributes, @ref Shaders::GenericGL contains generic definitions +for framebuffer outputs as well --- in many cases a shader has just one (color) +output, but some shaders such as @ref Shaders::FlatGL or @ref Shaders::PhongGL +offer an object ID output as well. A setup equivalent to what's done in Flat +shader's @ref Shaders-FlatGL-object-id but using the generic definitions would +look like this: @snippet MagnumShaders-gl.cpp shaders-generic-object-id */ diff --git a/doc/snippets/MagnumShaders-gl.cpp b/doc/snippets/MagnumShaders-gl.cpp index 96177ac94..0489a52a2 100644 --- a/doc/snippets/MagnumShaders-gl.cpp +++ b/doc/snippets/MagnumShaders-gl.cpp @@ -35,6 +35,7 @@ #include "Magnum/GL/DefaultFramebuffer.h" #include "Magnum/GL/Framebuffer.h" #include "Magnum/GL/Mesh.h" +#include "Magnum/GL/MeshView.h" #include "Magnum/GL/Shader.h" #include "Magnum/GL/Renderbuffer.h" #include "Magnum/GL/RenderbufferFormat.h" @@ -54,6 +55,16 @@ #include "Magnum/Shaders/VertexColorGL.h" #include "Magnum/Trade/LightData.h" +#ifndef MAGNUM_TARGET_GLES2 +#include "Magnum/GL/TextureArray.h" +#include "Magnum/Shaders/DistanceFieldVector.h" +#include "Magnum/Shaders/Flat.h" +#include "Magnum/Shaders/Generic.h" +#include "Magnum/Shaders/MeshVisualizer.h" +#include "Magnum/Shaders/Phong.h" +#include "Magnum/Shaders/Vector.h" +#endif + #define DOXYGEN_IGNORE(...) __VA_ARGS__ using namespace Magnum; @@ -86,29 +97,241 @@ mesh.addVertexBuffer(vertices, 0, //... ; /* [shaders-setup] */ +} +#endif -/* [shaders-rendering] */ -Matrix4 transformationMatrix, projectionMatrix; -GL::Texture2D diffuseTexture, specularTexture; +{ +GL::Mesh mesh; +/* [shaders-classic] */ +Matrix4 transformationMatrix{DOXYGEN_IGNORE()}, projectionMatrix{DOXYGEN_IGNORE()}; -Shaders::PhongGL shader{Shaders::PhongGL::Flag::DiffuseTexture}; -shader.bindDiffuseTexture(diffuseTexture) +Shaders::PhongGL shader; +shader .setTransformationMatrix(transformationMatrix) + .setProjectionMatrix(projectionMatrix) .setNormalMatrix(transformationMatrix.normalMatrix()) + .setDiffuseColor(0x2f83cc_rgbf) + .setLightColors({0xe9ecae_rgbf}) + .draw(mesh); +/* [shaders-classic] */ +} + +#ifndef MAGNUM_TARGET_GLES2 +{ +GL::Mesh mesh; +Matrix4 transformationMatrix, projectionMatrix; +/* [shaders-ubo] */ +GL::Buffer projectionUniform, lightUniform, materialUniform, + transformationUniform, drawUniform; +projectionUniform.setData({ + Shaders::ProjectionUniform3D{} + .setProjectionMatrix(projectionMatrix) +}); +lightUniform.setData({ + Shaders::PhongLightUniform{} + .setColor(0xe9ecae_rgbf) +}); +materialUniform.setData({ + Shaders::PhongMaterialUniform{} + .setDiffuseColor(0x2f83cc_rgbf) +}); +transformationUniform.setData({ + Shaders::TransformationUniform3D{} + .setTransformationMatrix(transformationMatrix) +}); +drawUniform.setData({ + Shaders::PhongDrawUniform{} + .setNormalMatrix(transformationMatrix.normalMatrix()) +}); + +Shaders::PhongGL shader{Shaders::PhongGL::Flag::UniformBuffers}; +shader + .bindProjectionBuffer(projectionUniform) + .bindLightBuffer(lightUniform) + .bindMaterialBuffer(materialUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .draw(mesh); +/* [shaders-ubo] */ +} + +{ +GL::Buffer projectionUniform, transformationUniform, drawUniform, lightUniform, + materialUniform; +/* [shaders-multi] */ +GL::Mesh redCone{DOXYGEN_IGNORE()}, yellowCube{DOXYGEN_IGNORE()}, redSphere{DOXYGEN_IGNORE()}; +Matrix4 redConeTransformation{DOXYGEN_IGNORE()}, + yellowCubeTransformation{DOXYGEN_IGNORE()}, + redSphereTransformation{DOXYGEN_IGNORE()}; + +materialUniform.setData({ + Shaders::PhongMaterialUniform{} + .setDiffuseColor(0xcd3431_rgbf), + Shaders::PhongMaterialUniform{} + .setDiffuseColor(0xc7cf2f_rgbf), +}); +transformationUniform.setData({ + Shaders::TransformationUniform3D{} + .setTransformationMatrix(redConeTransformation), + Shaders::TransformationUniform3D{} + .setTransformationMatrix(yellowCubeTransformation), + Shaders::TransformationUniform3D{} + .setTransformationMatrix(redSphereTransformation), +}); +drawUniform.setData({ + Shaders::PhongDrawUniform{} + .setNormalMatrix(redConeTransformation.normalMatrix()) + .setMaterialId(0), + Shaders::PhongDrawUniform{} + .setNormalMatrix(yellowCubeTransformation.normalMatrix()) + .setMaterialId(1), + Shaders::PhongDrawUniform{} + .setNormalMatrix(redSphereTransformation.normalMatrix()) + .setMaterialId(0), +}); + +/* One light, two materials, three draws */ +Shaders::PhongGL shader{Shaders::PhongGL::Flag::UniformBuffers, 1, 2, 3}; +shader + .bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindLightBuffer(lightUniform) + .bindMaterialBuffer(materialUniform) + .setDrawOffset(0) + .draw(redCone) + .setDrawOffset(1) + .draw(yellowCube) + .setDrawOffset(2) + .draw(redSphere); +/* [shaders-multi] */ +} + +{ +GL::Mesh mesh; +/* [shaders-multidraw] */ +GL::MeshView redConeView{DOXYGEN_IGNORE(mesh)}, yellowCubeView{DOXYGEN_IGNORE(mesh)}, redSphereView{DOXYGEN_IGNORE(mesh)}; +DOXYGEN_IGNORE() + +/* One light, two materials, three draws; with multidraw enabled */ +Shaders::PhongGL shader{Shaders::PhongGL::Flag::MultiDraw, 1, 2, 3}; +shader + DOXYGEN_IGNORE() + .draw({redConeView, yellowCubeView, redSphereView}); +/* [shaders-multidraw] */ +} +#endif + +{ +Matrix4 projectionMatrix; +/* [shaders-instancing] */ +Matrix4 redSphereTransformation{DOXYGEN_IGNORE()}, + yellowSphereTransformation{DOXYGEN_IGNORE()}, + greenSphereTransformation{DOXYGEN_IGNORE()}; + +struct { + Matrix4 transformationMatrix; + Matrix3x3 normalMatrix; + Color3 color; +} instanceData[]{ + {redSphereTransformation, + redSphereTransformation.normalMatrix(), + 0xcd3431_rgbf}, + {yellowSphereTransformation, + yellowSphereTransformation.normalMatrix(), + 0xc7cf2f_rgbf}, + {greenSphereTransformation, + greenSphereTransformation.normalMatrix(), + 0x3bd267_rgbf}, +}; + +GL::Mesh sphereInstanced{DOXYGEN_IGNORE()}; +sphereInstanced.addVertexBufferInstanced(GL::Buffer{instanceData}, 1, 0, + Shaders::PhongGL::TransformationMatrix{}, + Shaders::PhongGL::NormalMatrix{}, + Shaders::PhongGL::Color3{}); +sphereInstanced.setInstanceCount(3); + +Shaders::PhongGL shader{Shaders::PhongGL::Flag::InstancedTransformation| + Shaders::PhongGL::Flag::VertexColor}; +shader .setProjectionMatrix(projectionMatrix) + DOXYGEN_IGNORE() + .draw(sphereInstanced); +/* [shaders-instancing] */ +} + +{ +GL::Mesh mesh; +/* [shaders-textures] */ +GL::Texture2D diffuseTexture; +DOXYGEN_IGNORE() + +Shaders::PhongGL shader{Shaders::PhongGL::Flag::DiffuseTexture}; +shader.bindDiffuseTexture(diffuseTexture) + DOXYGEN_IGNORE() .draw(mesh); -/* [shaders-rendering] */ +/* [shaders-textures] */ +} +#ifndef MAGNUM_TARGET_GLES2 +{ +GL::Mesh mesh; +GL::MeshView redConeView{DOXYGEN_IGNORE(mesh)}, yellowCubeView{DOXYGEN_IGNORE(mesh)}, redSphereView{DOXYGEN_IGNORE(mesh)}; +/* [shaders-texture-arrays] */ +ImageView2D coneDiffuse{DOXYGEN_IGNORE({}, {})}, cubeDiffuse{DOXYGEN_IGNORE({}, {})}, sphereDiffuse{DOXYGEN_IGNORE({}, {})}; + +GL::Texture2DArray diffuseTexture; +diffuseTexture + DOXYGEN_IGNORE() + /* Assuming all iamges have the same format and size */ + .setStorage(1, GL::textureFormat(coneDiffuse.format()), + {coneDiffuse.size(), 3}) + .setSubImage(0, {}, coneDiffuse) + .setSubImage(1, {}, cubeDiffuse) + .setSubImage(2, {}, sphereDiffuse); + +GL::Buffer textureTransformationUniform; +textureTransformationUniform.setData({ + Shaders::TextureTransformationUniform{} + .setLayer(0), + Shaders::TextureTransformationUniform{} + .setLayer(1), + Shaders::TextureTransformationUniform{} + .setLayer(2), +}); + +Shaders::PhongGL shader{ + Shaders::PhongGL::Flag::MultiDraw| + Shaders::PhongGL::Flag::DiffuseTexture| + Shaders::PhongGL::Flag::TextureArrays, + 1, 2, 3}; +shader + DOXYGEN_IGNORE() + .bindDiffuseTexture(diffuseTexture) + .bindTextureTransformationBuffer(textureTransformationUniform) + .draw({redConeView, yellowCubeView, redSphereView}); +/* [shaders-texture-arrays] */ +} +#endif + +{ +GL::Buffer vertices; +GL::Mesh mesh; /* [shaders-generic] */ mesh.addVertexBuffer(vertices, 0, Shaders::GenericGL3D::Position{}, Shaders::GenericGL3D::Normal{}, Shaders::GenericGL3D::TextureCoordinates{}); /* [shaders-generic] */ +} +{ +GL::Mesh mesh; +Matrix4 transformationMatrix, projectionMatrix; /* [shaders-meshvisualizer] */ -Shaders::MeshVisualizerGL3D visualizerShader{Shaders::MeshVisualizerGL3D::Flag::Wireframe}; -visualizerShader +Shaders::MeshVisualizerGL3D shader{Shaders::MeshVisualizerGL3D::Flag::Wireframe}; +shader .setColor(0x2f83cc_rgbf) .setWireframeColor(0xdcdcdc_rgbf) .setViewportSize(Vector2{GL::defaultFramebuffer.viewport().size()}) @@ -118,6 +341,9 @@ visualizerShader /* [shaders-meshvisualizer] */ } +/* internal compiler error: in gimplify_init_constructor, at gimplify.c:4271 + on GCC 4.8 in the [60] array */ +#if !defined(__GNUC__) || defined(__clang__) || __GNUC__*100 + __GNUC_MINOR__ >= 500 { /* [DistanceFieldVectorGL-usage1] */ struct Vertex { @@ -150,12 +376,51 @@ Shaders::DistanceFieldVectorGL2D shader; shader.setColor(0x2f83cc_rgbf) .setOutlineColor(0xdcdcdc_rgbf) .setOutlineRange(0.6f, 0.4f) - .bindVectorTexture(texture) .setTransformationProjectionMatrix(projectionMatrix*transformationMatrix) + .bindVectorTexture(texture) .draw(mesh); /* [DistanceFieldVectorGL-usage2] */ } +#endif + +#ifndef MAGNUM_TARGET_GLES2 +{ +GL::Mesh mesh; +Matrix3 transformationMatrix, projectionMatrix; +GL::Texture2D texture; +/* [DistanceFieldVectorGL-ubo] */ +GL::Buffer projectionTransformationUniform, materialUniform, drawUniform; +projectionTransformationUniform.setData({ + Shaders::TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix(transformationMatrix*projectionMatrix) +}); +materialUniform.setData({ + Shaders::DistanceFieldVectorMaterialUniform{} + .setColor(0x2f83cc_rgbf) + .setOutlineColor(0xdcdcdc_rgbf) + .setOutlineRange(0.6f, 0.4f) +}); +drawUniform.setData({ + Shaders::DistanceFieldVectorDrawUniform{} + .setMaterialId(0) +}); + +Shaders::DistanceFieldVectorGL2D shader{ + Shaders::DistanceFieldVectorGL2D::Flag::UniformBuffers +}; +shader + .bindTransformationProjectionBuffer(projectionTransformationUniform) + .bindMaterialBuffer(materialUniform) + .bindDrawBuffer(drawUniform) + .bindVectorTexture(texture) + .draw(mesh); +/* [DistanceFieldVectorGL-ubo] */ +} +#endif +/* internal compiler error: in gimplify_init_constructor, at gimplify.c:4271 + on GCC 4.8 in the [60] array */ +#if !defined(__GNUC__) || defined(__clang__) || __GNUC__*100 + __GNUC_MINOR__ >= 500 { /* [FlatGL-usage-colored1] */ struct Vertex { @@ -217,6 +482,7 @@ shader.setTransformationProjectionMatrix(projectionMatrix*transformationMatrix) .draw(mesh); /* [FlatGL-usage-textured2] */ } +#endif #ifndef MAGNUM_TARGET_GLES2 { @@ -274,6 +540,35 @@ mesh.setInstanceCount(Containers::arraySize(instanceData)) /* [FlatGL-usage-instancing] */ } +#ifndef MAGNUM_TARGET_GLES2 +{ +GL::Mesh mesh; +Matrix4 transformationMatrix, projectionMatrix; +/* [FlatGL-ubo] */ +GL::Buffer projectionTransformationUniform, materialUniform, drawUniform; +projectionTransformationUniform.setData({ + Shaders::TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix(transformationMatrix*projectionMatrix) +}); +materialUniform.setData({ + Shaders::FlatMaterialUniform{} + .setColor(0x2f83cc_rgbf) +}); +drawUniform.setData({ + Shaders::FlatDrawUniform{} + .setMaterialId(0) +}); + +Shaders::FlatGL3D shader{Shaders::FlatGL3D::Flag::UniformBuffers}; +shader + .bindTransformationProjectionBuffer(projectionTransformationUniform) + .bindMaterialBuffer(materialUniform) + .bindDrawBuffer(drawUniform) + .draw(mesh); +/* [FlatGL-ubo] */ +} +#endif + { struct: GL::AbstractShaderProgram { void foo() { @@ -320,6 +615,9 @@ mesh.setInstanceCount(Containers::arraySize(instanceData)) /* [PhongGL-usage-instancing] */ } +/* internal compiler error: in gimplify_init_constructor, at gimplify.c:4271 + on GCC 4.8 in the [60] array */ +#if !defined(__GNUC__) || defined(__clang__) || __GNUC__*100 + __GNUC_MINOR__ >= 500 { /* [MeshVisualizerGL3D-usage-geom1] */ struct Vertex { @@ -463,6 +761,47 @@ shader.setColorMapTransformation(0.0f, 1.0f/Math::max(objectIds)) } #endif +#ifndef MAGNUM_TARGET_GLES2 +{ +GL::Mesh mesh; +Matrix4 transformationMatrix, projectionMatrix; +GL::Texture2D texture; +/* [MeshVisualizerGL3D-ubo] */ +GL::Buffer projectionUniform, materialUniform, transformationUniform, + drawUniform; +projectionUniform.setData({ + Shaders::ProjectionUniform3D{} + .setProjectionMatrix(projectionMatrix) +}); +materialUniform.setData({ + Shaders::MeshVisualizerMaterialUniform{} + .setColor(0x2f83cc_rgbf) + .setWireframeColor(0xdcdcdc_rgbf) +}); +transformationUniform.setData({ + Shaders::TransformationUniform3D{} + .setTransformationMatrix(transformationMatrix) +}); +drawUniform.setData({ + Shaders::MeshVisualizerDrawUniform3D{} + .setMaterialId(0) +}); + +Shaders::MeshVisualizerGL3D shader{ + Shaders::MeshVisualizerGL3D::Flag::Wireframe| + Shaders::MeshVisualizerGL3D::Flag::UniformBuffers +}; +shader + .setViewportSize(Vector2{GL::defaultFramebuffer.viewport().size()}) + .bindProjectionBuffer(projectionUniform) + .bindMaterialBuffer(materialUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .draw(mesh); +/* [MeshVisualizerGL3D-ubo] */ +} +#endif + #if !defined(__GNUC__) || defined(__clang__) || __GNUC__*100 + __GNUC_MINOR__ >= 500 { /* [PhongGL-usage-colored1] */ @@ -580,6 +919,48 @@ shader.bindTextures(&diffuseAlphaTexture, &diffuseAlphaTexture, nullptr, nullptr /* [PhongGL-usage-alpha] */ } +#ifndef MAGNUM_TARGET_GLES2 +{ +GL::Mesh mesh; +Matrix4 transformationMatrix, projectionMatrix; +GL::Texture2D texture; +/* [PhongGL-ubo] */ +GL::Buffer projectionUniform, lightUniform, materialUniform, + transformationUniform, drawUniform; +projectionUniform.setData({ + Shaders::ProjectionUniform3D{} + .setProjectionMatrix(projectionMatrix) +}); +lightUniform.setData({ + Shaders::PhongLightUniform{} +}); +materialUniform.setData({ + Shaders::PhongMaterialUniform{} + .setDiffuseColor(0x2f83cc_rgbf) + .setShininess(200.0f) +}); +transformationUniform.setData({ + Shaders::TransformationUniform3D{} + .setTransformationMatrix(transformationMatrix) +}); +drawUniform.setData({ + Shaders::PhongDrawUniform{} + .setNormalMatrix(transformationMatrix.normalMatrix()) + .setMaterialId(0) +}); + +Shaders::PhongGL shader{Shaders::PhongGL::Flag::UniformBuffers}; +shader + .bindProjectionBuffer(projectionUniform) + .bindLightBuffer(lightUniform) + .bindMaterialBuffer(materialUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .draw(mesh); +/* [PhongGL-ubo] */ +} +#endif + #if !defined(__GNUC__) || defined(__clang__) || __GNUC__*100 + __GNUC_MINOR__ >= 500 { /* [VectorGL-usage1] */ @@ -611,7 +992,40 @@ shader.setColor(0x2f83cc_rgbf) .draw(mesh); /* [VectorGL-usage2] */ } +#endif +#ifndef MAGNUM_TARGET_GLES2 +{ +GL::Mesh mesh; +Matrix3 transformationMatrix, projectionMatrix; +GL::Texture2D texture; +/* [VectorGL-ubo] */ +GL::Buffer projectionTransformationUniform, materialUniform, drawUniform; +projectionTransformationUniform.setData({ + Shaders::TransformationProjectionUniform2D{} + .setTransformationProjectionMatrix(transformationMatrix*projectionMatrix) +}); +materialUniform.setData({ + Shaders::VectorMaterialUniform{} + .setColor(0x2f83cc_rgbf) +}); +drawUniform.setData({ + Shaders::VectorDrawUniform{} + .setMaterialId(0) +}); + +Shaders::VectorGL2D shader{Shaders::VectorGL2D::Flag::UniformBuffers}; +shader + .bindTransformationProjectionBuffer(projectionTransformationUniform) + .bindMaterialBuffer(materialUniform) + .bindDrawBuffer(drawUniform) + .bindVectorTexture(texture) + .draw(mesh); +/* [VectorGL-ubo] */ +} +#endif + +#if !defined(__GNUC__) || defined(__clang__) || __GNUC__*100 + __GNUC_MINOR__ >= 500 { /* [VertexColorGL-usage1] */ struct Vertex { @@ -643,4 +1057,23 @@ shader.setTransformationProjectionMatrix(projectionMatrix*transformationMatrix) } #endif +#ifndef MAGNUM_TARGET_GLES2 +{ +GL::Mesh mesh; +Matrix4 transformationMatrix, projectionMatrix; +/* [VertexColorGL-ubo] */ +GL::Buffer projectionTransformationUniform; +projectionTransformationUniform.setData({ + Shaders::TransformationProjectionUniform3D{} + .setTransformationProjectionMatrix(transformationMatrix*projectionMatrix) +}); + +Shaders::VertexColorGL3D shader{Shaders::VertexColorGL3D::Flag::UniformBuffers}; +shader + .bindTransformationProjectionBuffer(projectionTransformationUniform) + .draw(mesh); +/* [VertexColorGL-ubo] */ +} +#endif + } diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.h b/src/Magnum/Shaders/DistanceFieldVectorGL.h index abd2e926c..84e383bff 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.h +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.h @@ -80,6 +80,39 @@ Common rendering setup: @snippet MagnumShaders-gl.cpp DistanceFieldVectorGL-usage2 +@section Shaders-DistanceFieldVectorGL-ubo Uniform buffers + +See @ref shaders-usage-ubo for a high-level overview that applies to all +shaders. In this particular case, because the shader doesn't need a separate +projection and transformation matrix, a combined one is supplied via a +@ref TransformationProjectionUniform2D / @ref TransformationProjectionUniform3D +buffer. To maximize use of the limited uniform buffer memory, materials are +supplied separately in a @ref DistanceFieldVectorMaterialUniform buffer and +then referenced via @relativeref{DistanceFieldVectorDrawUniform,materialId} +from a @ref DistanceFieldVectorDrawUniform; for optional texture transformation +a per-draw @ref TextureTransformationUniform can be supplied as well. A uniform +buffer setup equivalent to the above would look like this: + +@snippet MagnumShaders-gl.cpp DistanceFieldVectorGL-ubo + +For a multidraw workflow enable @ref Flag::MultiDraw, supply desired material +and draw count in the @ref DistanceFieldVectorGL(Flags, UnsignedInt, UnsignedInt) +constructor and specify material references and texture offsets for every draw. +Texture arrays aren't currently supported for this shader. Besides that, the +usage is similar for all shaders, see @ref shaders-usage-multidraw for an +example. + +@requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} for uniform + buffers. +@requires_gl46 Extension @gl_extension{ARB,shader_draw_parameters} for + multidraw. +@requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. +@requires_webgl20 Uniform buffers are not available in WebGL 1.0. +@requires_es_extension Extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + (unlisted) for multidraw. +@requires_webgl_extension Extension @webgl_extension{ANGLE,multi_draw} for + multidraw. + @see @ref shaders, @ref DistanceFieldVectorGL2D, @ref DistanceFieldVectorGL3D @todo Use fragment shader derivations to have proper smoothness in perspective/ large zoom levels, make it optional as it might have negative performance diff --git a/src/Magnum/Shaders/FlatGL.h b/src/Magnum/Shaders/FlatGL.h index d57785615..caf4dd6b0 100644 --- a/src/Magnum/Shaders/FlatGL.h +++ b/src/Magnum/Shaders/FlatGL.h @@ -156,6 +156,41 @@ color to a mesh: @requires_webgl20 Extension @webgl_extension{ANGLE,instanced_arrays} in WebGL 1.0. +@section Shaders-FlatGL-ubo Uniform buffers + +See @ref shaders-usage-ubo for a high-level overview that applies to all +shaders. In this particular case, because the shader doesn't need a separate +projection and transformation matrix, a combined one is supplied via a +@ref TransformationProjectionUniform2D / @ref TransformationProjectionUniform3D +buffer. To maximize use of the limited uniform buffer memory, materials are +supplied separately in a @ref FlatMaterialUniform buffer and then referenced +via @relativeref{FlatDrawUniform,materialId} from a @ref FlatDrawUniform; for +optional texture transformation a per-draw @ref TextureTransformationUniform +can be supplied as well. A uniform buffer setup equivalent to the +@ref Shaders-FlatGL-colored "colored case at the top" would look like this: + +@snippet MagnumShaders-gl.cpp FlatGL-ubo + +For a multidraw workflow enable @ref Flag::MultiDraw (and possibly +@ref Flag::TextureArrays), supply desired material and draw count in the +@ref FlatGL(Flags, UnsignedInt, UnsignedInt) constructor and specify material +references and texture offsets/layers for every draw. The usage is similar for +all shaders, see @ref shaders-usage-multidraw for an example. + +@requires_gl30 Extension @gl_extension{EXT,texture_array} for texture arrays. +@requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} for uniform + buffers. +@requires_gl46 Extension @gl_extension{ARB,shader_draw_parameters} for + multidraw. +@requires_gles30 Neither texture arrays nor uniform buffers are available in + OpenGL ES 2.0. +@requires_webgl20 Neither texture arrays nor uniform buffers are available in + WebGL 1.0. +@requires_es_extension Extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + (unlisted) for multidraw. +@requires_webgl_extension Extension @webgl_extension{ANGLE,multi_draw} for + multidraw. + @see @ref shaders, @ref FlatGL2D, @ref FlatGL3D */ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL::AbstractShaderProgram { diff --git a/src/Magnum/Shaders/MeshVisualizerGL.h b/src/Magnum/Shaders/MeshVisualizerGL.h index fccf0cd88..6d8967594 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.h +++ b/src/Magnum/Shaders/MeshVisualizerGL.h @@ -781,6 +781,37 @@ non-indexed @ref MeshPrimitive::Triangles. @requires_gles `gl_PrimitiveID` is not available in WebGL. @requires_webgl20 `gl_VertexID` is not available in WebGL 1.0. +@section Shaders-MeshVisualizerGL3D-ubo Uniform buffers + +See @ref shaders-usage-ubo for a high-level overview that applies to all +shaders. In this particular case, the shader needs a separate +@ref ProjectionUniform3D and @ref TransformationUniform3D buffer. To maximize +use of the limited uniform buffer memory, materials are supplied separately in +a @ref MeshVisualizerMaterialUniform and then referenced via +@relativeref{MeshVisualizerDrawUniform3D,materialId} from a +@ref MeshVisualizerDrawUniform3D. A uniform buffer setup equivalent to the +@ref Shaders-MeshVisualizerGL3D-wireframe "wireframe case at the top" would +look like this --- note that @ref setViewportSize() is an immediate uniform +here as well, as it's assumed to be set globally and rarely changed: + +@snippet MagnumShaders-gl.cpp MeshVisualizerGL3D-ubo + +For a multidraw workflow enable @ref Flag::MultiDraw, supply desired material +and draw count in the @ref MeshVisualizerGL3D(Flags, UnsignedInt, UnsignedInt) +constructor and specify material references for every draw. The usage is +similar for all shaders, see @ref shaders-usage-multidraw for an example. + +@requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} for uniform + buffers. +@requires_gl46 Extension @gl_extension{ARB,shader_draw_parameters} for + multidraw. +@requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. +@requires_webgl20 Uniform buffers are not available in WebGL 1.0. +@requires_es_extension Extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + (unlisted) for multidraw. +@requires_webgl_extension Extension @webgl_extension{ANGLE,multi_draw} for + multidraw. + @see @ref shaders, @ref MeshVisualizerGL2D @todo Understand and add support wireframe width/smoothness without GS */ diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index 9c34a3b2d..89da15b7f 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -248,6 +248,44 @@ well to ensure lighting works: @requires_webgl20 Extension @webgl_extension{ANGLE,instanced_arrays} in WebGL 1.0. +@section Shaders-PhongGL-ubo Uniform buffers + +See @ref shaders-usage-ubo for a high-level overview that applies to all +shaders. In this particular case, the shader needs a separate +@ref ProjectionUniform3D and @ref TransformationUniform3D buffer, lights are +supplied via a @ref PhongLightUniform. To maximize use of the limited uniform +buffer memory, materials are supplied separately in a @ref PhongMaterialUniform +buffer and then referenced via @relativeref{PhongDrawUniform,materialId} from a +@ref PhongDrawUniform; for optional texture transformation a per-draw +@ref TextureTransformationUniform can be supplied as well. A uniform buffer +setup equivalent to the @ref Shaders-PhongGL-colored "colored case at the top", +with one default light, would look like this: + +@snippet MagnumShaders-gl.cpp PhongGL-ubo + +For a multidraw workflow enable @ref Flag::MultiDraw (and possibly +@ref Flag::TextureArrays) and supply desired light, material and draw count in +the @ref PhongGL(Flags, UnsignedInt, UnsignedInt, UnsignedInt) constructor. For +every draw then specify material references and texture offsets/layers, it's +also possible to perform per-draw light culling by supplying a subrange into +the @ref PhongLightUniform array using @ref PhongDrawUniform::lightOffset and +@relativeref{PhongDrawUniform,lightCount}. Besides that, the usage is similar +for all shaders, see @ref shaders-usage-multidraw for an example. + +@requires_gl30 Extension @gl_extension{EXT,texture_array} for texture arrays. +@requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} for uniform + buffers. +@requires_gl46 Extension @gl_extension{ARB,shader_draw_parameters} for + multidraw. +@requires_gles30 Neither texture arrays nor uniform buffers are available in + OpenGL ES 2.0. +@requires_webgl20 Neither texture arrays nor uniform buffers are available in + WebGL 1.0. +@requires_es_extension Extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + (unlisted) for multidraw. +@requires_webgl_extension Extension @webgl_extension{ANGLE,multi_draw} for + multidraw. + @see @ref shaders */ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { diff --git a/src/Magnum/Shaders/VectorGL.h b/src/Magnum/Shaders/VectorGL.h index 977ec4a26..ad3160304 100644 --- a/src/Magnum/Shaders/VectorGL.h +++ b/src/Magnum/Shaders/VectorGL.h @@ -78,6 +78,39 @@ Common rendering setup: @snippet MagnumShaders-gl.cpp VectorGL-usage2 +@section Shaders-VectorGL-ubo Uniform buffers + +See @ref shaders-usage-ubo for a high-level overview that applies to all +shaders. In this particular case, because the shader doesn't need a separate +projection and transformation matrix, a combined one is supplied via a +@ref TransformationProjectionUniform2D / @ref TransformationProjectionUniform3D +buffer. To maximize use of the limited uniform buffer memory, materials are +supplied separately in a @ref VectorMaterialUniform buffer and then referenced +via @relativeref{VectorDrawUniform,materialId} from a @ref VectorDrawUniform; +for optional texture transformation a per-draw +@ref TextureTransformationUniform can be supplied as well. A uniform buffer +setup equivalent to the above would look like this: + +@snippet MagnumShaders-gl.cpp VectorGL-ubo + +For a multidraw workflow enable @ref Flag::MultiDraw, supply desired material +and draw count in the @ref VectorGL(Flags, UnsignedInt, UnsignedInt) +constructor and specify material references and texture offsets for every draw. +Texture arrays aren't currently supported for this shader. Besides that, the +usage is similar for all shaders, see @ref shaders-usage-multidraw for an +example. + +@requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} for uniform + buffers. +@requires_gl46 Extension @gl_extension{ARB,shader_draw_parameters} for + multidraw. +@requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. +@requires_webgl20 Uniform buffers are not available in WebGL 1.0. +@requires_es_extension Extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + (unlisted) for multidraw. +@requires_webgl_extension Extension @webgl_extension{ANGLE,multi_draw} for + multidraw. + @see @ref shaders, @ref VectorGL2D, @ref VectorGL3D */ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL::AbstractShaderProgram { diff --git a/src/Magnum/Shaders/VertexColorGL.h b/src/Magnum/Shaders/VertexColorGL.h index 4379062cd..c71c9688b 100644 --- a/src/Magnum/Shaders/VertexColorGL.h +++ b/src/Magnum/Shaders/VertexColorGL.h @@ -79,6 +79,33 @@ Common rendering setup: @snippet MagnumShaders-gl.cpp VertexColorGL-usage2 +@section Shaders-VertexColorGL-ubo Uniform buffers + +See @ref shaders-usage-ubo for a high-level overview that applies to all +shaders. In this particular case, because the shader doesn't need a separate +projection and transformation matrix, a combined one is supplied via a +@ref TransformationProjectionUniform2D / @ref TransformationProjectionUniform3D +buffer. This is also the only buffer supplied, as there are no other draw +parameters. A uniform buffer setup equivalent to the above would look like +this: + +@snippet MagnumShaders-gl.cpp VectorGL-ubo + +For a multidraw workflow enable @ref Flag::MultiDraw and supply desired draw +count in the @ref VertexColorGL(Flags, UnsignedInt) constructor. The usage is +similar for all shaders, see @ref shaders-usage-multidraw for an example. + +@requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} for uniform + buffers. +@requires_gl46 Extension @gl_extension{ARB,shader_draw_parameters} for + multidraw. +@requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. +@requires_webgl20 Uniform buffers are not available in WebGL 1.0. +@requires_es_extension Extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) + (unlisted) for multidraw. +@requires_webgl_extension Extension @webgl_extension{ANGLE,multi_draw} for + multidraw. + @see @ref shaders, @ref VertexColorGL2D, @ref VertexColorGL3D */ template class MAGNUM_SHADERS_EXPORT VertexColorGL: public GL::AbstractShaderProgram { From 380441cd665fc6d0541ad6f235e253f6f8bbd3c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 8 Jun 2021 11:17:41 +0200 Subject: [PATCH 53/93] doc: this page was missing the footer prev/next/up navigation. --- doc/file-formats.dox | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/file-formats.dox b/doc/file-formats.dox index 40980c65b..fef30737c 100644 --- a/doc/file-formats.dox +++ b/doc/file-formats.dox @@ -28,6 +28,7 @@ namespace Magnum { @brief Support tables for widely used image, scene, audio and font formats @tableofcontents +@m_footernavigation The @ref Trade::AnyImageImporter "AnyImageImporter", @ref Trade::AnySceneImporter "AnySceneImporter", From c92d608aa712a7344b8f715bbf0de80f5ea2ddee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 8 Jun 2021 11:44:28 +0200 Subject: [PATCH 54/93] Shaders: assume drawOffset is 0 if DRAW_COUNT is 1. This is always true in the single-draw case, since setDrawOffset() asserts on this. In the multi-draw case this optimization doesn't make sense, because it doesn't make sense to create a multidraw shader with just one draw. On an Intel 630 GPU this resulted in single-draw single-material Phong to go from 550 ms to 440, which is roughly a 20% improvement. For the simpler shaders the difference is even higher. The multidraw numbers stayed the same as before, obviously. --- src/Magnum/Shaders/DistanceFieldVector.frag | 4 ++++ src/Magnum/Shaders/DistanceFieldVectorGL.cpp | 4 ++-- src/Magnum/Shaders/DistanceFieldVectorGL.h | 4 +++- src/Magnum/Shaders/Flat.frag | 4 ++++ src/Magnum/Shaders/Flat.vert | 4 ++++ src/Magnum/Shaders/FlatGL.cpp | 4 ++-- src/Magnum/Shaders/FlatGL.h | 4 +++- src/Magnum/Shaders/MeshVisualizer.frag | 4 ++++ src/Magnum/Shaders/MeshVisualizer.vert | 4 ++++ src/Magnum/Shaders/MeshVisualizerGL.cpp | 6 +++--- src/Magnum/Shaders/MeshVisualizerGL.h | 7 +++++-- src/Magnum/Shaders/Phong.frag | 4 ++++ src/Magnum/Shaders/Phong.vert | 4 ++++ src/Magnum/Shaders/PhongGL.cpp | 4 ++-- src/Magnum/Shaders/PhongGL.h | 4 +++- src/Magnum/Shaders/Vector.frag | 4 ++++ src/Magnum/Shaders/Vector.vert | 4 ++++ src/Magnum/Shaders/VectorGL.cpp | 4 ++-- src/Magnum/Shaders/VectorGL.h | 4 +++- src/Magnum/Shaders/VertexColor.vert | 4 ++++ src/Magnum/Shaders/VertexColorGL.cpp | 4 ++-- src/Magnum/Shaders/VertexColorGL.h | 4 +++- 22 files changed, 73 insertions(+), 20 deletions(-) diff --git a/src/Magnum/Shaders/DistanceFieldVector.frag b/src/Magnum/Shaders/DistanceFieldVector.frag index ab6733f4f..0421f17a1 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.frag +++ b/src/Magnum/Shaders/DistanceFieldVector.frag @@ -72,6 +72,7 @@ uniform lowp float smoothness #else #ifndef MULTI_DRAW +#if DRAW_COUNT > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -80,6 +81,9 @@ uniform highp uint drawOffset = 0u #endif ; +#else +#define drawOffset 0u +#endif #define drawId drawOffset #endif diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp index 2a9e55175..381a6b2f6 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp @@ -165,7 +165,7 @@ template DistanceFieldVectorGL::DistanceFiel { #ifndef MAGNUM_TARGET_GLES2 if(flags >= Flag::UniformBuffers) { - _drawOffsetUniform = uniformLocation("drawOffset"); + if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset"); } else #endif { @@ -280,7 +280,7 @@ template DistanceFieldVectorGL& DistanceFiel "Shaders::DistanceFieldVectorGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); CORRADE_ASSERT(offset < _drawCount, "Shaders::DistanceFieldVectorGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); - setUniform(_drawOffsetUniform, offset); + if(_drawCount > 1) setUniform(_drawOffsetUniform, offset); return *this; } diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.h b/src/Magnum/Shaders/DistanceFieldVectorGL.h index 84e383bff..69290b331 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.h +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.h @@ -445,7 +445,9 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector * @ref bindTransformationProjectionBuffer(), @ref bindDrawBuffer() and * @ref bindTextureTransformationBuffer() should be used for current * draw. Expects that @ref Flag::UniformBuffers is set and @p offset is - * less than @ref drawCount(). Initial value is @cpp 0 @ce. + * less than @ref drawCount(). Initial value is @cpp 0 @ce, if + * @ref drawCount() is @cpp 1 @ce, the function is a no-op as the + * shader assumes draw offset to be always zero. * * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this * value, which makes each draw submitted via diff --git a/src/Magnum/Shaders/Flat.frag b/src/Magnum/Shaders/Flat.frag index 6b783be40..8f5e5a6d7 100644 --- a/src/Magnum/Shaders/Flat.frag +++ b/src/Magnum/Shaders/Flat.frag @@ -72,6 +72,7 @@ uniform highp uint objectId; /* defaults to zero */ #else #ifndef MULTI_DRAW +#if DRAW_COUNT > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -80,6 +81,9 @@ uniform highp uint drawOffset = 0u #endif ; +#else +#define drawOffset 0u +#endif #define drawId drawOffset #endif diff --git a/src/Magnum/Shaders/Flat.vert b/src/Magnum/Shaders/Flat.vert index 71865a578..dfae3fcf1 100644 --- a/src/Magnum/Shaders/Flat.vert +++ b/src/Magnum/Shaders/Flat.vert @@ -92,6 +92,7 @@ uniform highp uint textureLayer; /* defaults to zero */ /* Uniform buffers */ #else +#if DRAW_COUNT > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -100,6 +101,9 @@ uniform highp uint drawOffset = 0u #endif ; +#else +#define drawOffset 0u +#endif layout(std140 #ifdef EXPLICIT_BINDING diff --git a/src/Magnum/Shaders/FlatGL.cpp b/src/Magnum/Shaders/FlatGL.cpp index c65fdd11d..cf281c50d 100644 --- a/src/Magnum/Shaders/FlatGL.cpp +++ b/src/Magnum/Shaders/FlatGL.cpp @@ -216,7 +216,7 @@ template FlatGL::FlatGL(const Flags flags { #ifndef MAGNUM_TARGET_GLES2 if(flags >= Flag::UniformBuffers) { - _drawOffsetUniform = uniformLocation("drawOffset"); + if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset"); } else #endif { @@ -342,7 +342,7 @@ template FlatGL& FlatGL::setDraw "Shaders::FlatGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); CORRADE_ASSERT(offset < _drawCount, "Shaders::FlatGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); - setUniform(_drawOffsetUniform, offset); + if(_drawCount > 1) setUniform(_drawOffsetUniform, offset); return *this; } diff --git a/src/Magnum/Shaders/FlatGL.h b/src/Magnum/Shaders/FlatGL.h index caf4dd6b0..ffca52dac 100644 --- a/src/Magnum/Shaders/FlatGL.h +++ b/src/Magnum/Shaders/FlatGL.h @@ -755,7 +755,9 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * @ref bindTransformationProjectionBuffer(), @ref bindDrawBuffer() and * @ref bindTextureTransformationBuffer() should be used for current * draw. Expects that @ref Flag::UniformBuffers is set and @p offset is - * less than @ref drawCount(). Initial value is @cpp 0 @ce. + * less than @ref drawCount(). Initial value is @cpp 0 @ce, if + * @ref drawCount() is @cpp 1 @ce, the function is a no-op as the + * shader assumes draw offset to be always zero. * * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this * value, which makes each draw submitted via diff --git a/src/Magnum/Shaders/MeshVisualizer.frag b/src/Magnum/Shaders/MeshVisualizer.frag index 6987931ba..20bb89ec7 100644 --- a/src/Magnum/Shaders/MeshVisualizer.frag +++ b/src/Magnum/Shaders/MeshVisualizer.frag @@ -111,6 +111,7 @@ uniform lowp vec2 colorMapOffsetScale #else #ifndef MULTI_DRAW +#if DRAW_COUNT > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 1) #endif @@ -119,6 +120,9 @@ uniform highp uint drawOffset = 0u #endif ; +#else +#define drawOffset 0u +#endif #define drawId drawOffset #endif diff --git a/src/Magnum/Shaders/MeshVisualizer.vert b/src/Magnum/Shaders/MeshVisualizer.vert index 6aa9ebe7b..c0e514e75 100644 --- a/src/Magnum/Shaders/MeshVisualizer.vert +++ b/src/Magnum/Shaders/MeshVisualizer.vert @@ -113,6 +113,7 @@ uniform highp float lineLength /* Uniform buffers */ #else +#if DRAW_COUNT > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 1) #endif @@ -121,6 +122,9 @@ uniform highp uint drawOffset = 0u #endif ; +#else +#define drawOffset 0u +#endif #ifdef TWO_DIMENSIONS layout(std140 diff --git a/src/Magnum/Shaders/MeshVisualizerGL.cpp b/src/Magnum/Shaders/MeshVisualizerGL.cpp index 99cfa37f1..1fc40b8ec 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.cpp +++ b/src/Magnum/Shaders/MeshVisualizerGL.cpp @@ -263,7 +263,7 @@ MeshVisualizerGLBase& MeshVisualizerGLBase::setDrawOffset(const UnsignedInt offs "Shaders::MeshVisualizerGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); CORRADE_ASSERT(offset < _drawCount, "Shaders::MeshVisualizerGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); - setUniform(_drawOffsetUniform, offset); + if(_drawCount > 1) setUniform(_drawOffsetUniform, offset); return *this; } @@ -426,7 +426,7 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(const Flags flags #ifndef MAGNUM_TARGET_GLES2 if(flags >= Flag::UniformBuffers) { - _drawOffsetUniform = uniformLocation("drawOffset"); + if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset"); } else #endif { @@ -757,7 +757,7 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(const Flags flags #ifndef MAGNUM_TARGET_GLES2 if(flags >= Flag::UniformBuffers) { - _drawOffsetUniform = uniformLocation("drawOffset"); + if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset"); } else #endif { diff --git a/src/Magnum/Shaders/MeshVisualizerGL.h b/src/Magnum/Shaders/MeshVisualizerGL.h index 6d8967594..878418692 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.h +++ b/src/Magnum/Shaders/MeshVisualizerGL.h @@ -495,7 +495,9 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * @ref bindTransformationProjectionBuffer() and @ref bindDrawBuffer() * should be used for current draw. Expects that * @ref Flag::UniformBuffers is set and @p offset is less than - * @ref drawCount(). Initial value is @cpp 0 @ce. + * @ref drawCount(). Initial value is @cpp 0 @ce, if @ref drawCount() + * is @cpp 1 @ce, the function is a no-op as the shader assumes draw + * offset to be always zero. * * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this * value, which makes each draw submitted via @@ -1490,7 +1492,8 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * @ref bindTransformationBuffer() and @ref bindDrawBuffer() should be * used for current draw. Expects that @ref Flag::UniformBuffers is set * and @p offset is less than @ref drawCount(). Initial value is - * @cpp 0 @ce. + * @cpp 0 @ce, if @ref drawCount() is @cpp 1 @ce, the function is a + * no-op as the shader assumes draw offset to be always zero. * * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this * value, which makes each draw submitted via diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index 1a1827068..57ab5b2d5 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -149,6 +149,7 @@ uniform lowp float lightRanges[LIGHT_COUNT] #else #ifndef MULTI_DRAW +#if DRAW_COUNT > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -157,6 +158,9 @@ uniform highp uint drawOffset = 0u #endif ; +#else +#define drawOffset 0u +#endif #define drawId drawOffset #endif diff --git a/src/Magnum/Shaders/Phong.vert b/src/Magnum/Shaders/Phong.vert index e31635031..521c55389 100644 --- a/src/Magnum/Shaders/Phong.vert +++ b/src/Magnum/Shaders/Phong.vert @@ -114,6 +114,7 @@ uniform highp vec4 lightPositions[LIGHT_COUNT] /* Uniform buffers */ #else +#if DRAW_COUNT > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -122,6 +123,9 @@ uniform highp uint drawOffset = 0u #endif ; +#else +#define drawOffset 0u +#endif /* Keep in sync with Phong.frag. Can't "outsource" to a common file because the #extension directive needs to be always before any code. */ diff --git a/src/Magnum/Shaders/PhongGL.cpp b/src/Magnum/Shaders/PhongGL.cpp index 30a9eceb9..0190c050e 100644 --- a/src/Magnum/Shaders/PhongGL.cpp +++ b/src/Magnum/Shaders/PhongGL.cpp @@ -326,7 +326,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount { #ifndef MAGNUM_TARGET_GLES2 if(flags >= Flag::UniformBuffers) { - _drawOffsetUniform = uniformLocation("drawOffset"); + if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset"); } else #endif { @@ -706,7 +706,7 @@ PhongGL& PhongGL::setDrawOffset(const UnsignedInt offset) { "Shaders::PhongGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); CORRADE_ASSERT(offset < _drawCount, "Shaders::PhongGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); - setUniform(_drawOffsetUniform, offset); + if(_drawCount > 1) setUniform(_drawOffsetUniform, offset); return *this; } diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index 89da15b7f..aef2d67d2 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -1296,7 +1296,9 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * bound with @ref bindTransformationBuffer(), @ref bindDrawBuffer() * and @ref bindTextureTransformationBuffer() should be used for * current draw. Expects that @ref Flag::UniformBuffers is set and - * @p offset is less than @ref drawCount(). Initial value is @cpp 0 @ce. + * @p offset is less than @ref drawCount(). Initial value is + * @cpp 0 @ce, if @ref drawCount() is @cpp 1 @ce, the function is a + * no-op as the shader assumes draw offset to be always zero. * * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this * value, which makes each draw submitted via diff --git a/src/Magnum/Shaders/Vector.frag b/src/Magnum/Shaders/Vector.frag index a36e3a7a6..1b78de18f 100644 --- a/src/Magnum/Shaders/Vector.frag +++ b/src/Magnum/Shaders/Vector.frag @@ -54,6 +54,7 @@ uniform lowp vec4 color #else #ifndef MULTI_DRAW +#if DRAW_COUNT > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -62,6 +63,9 @@ uniform highp uint drawOffset = 0u #endif ; +#else +#define drawOffset 0u +#endif #define drawId drawOffset #endif diff --git a/src/Magnum/Shaders/Vector.vert b/src/Magnum/Shaders/Vector.vert index 3f38ac103..f92fe714b 100644 --- a/src/Magnum/Shaders/Vector.vert +++ b/src/Magnum/Shaders/Vector.vert @@ -76,6 +76,7 @@ uniform mediump mat3 textureMatrix /* Uniform buffers */ #else +#if DRAW_COUNT > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -84,6 +85,9 @@ uniform highp uint drawOffset = 0u #endif ; +#else +#define drawOffset 0u +#endif layout(std140 #ifdef EXPLICIT_BINDING diff --git a/src/Magnum/Shaders/VectorGL.cpp b/src/Magnum/Shaders/VectorGL.cpp index bd98a6fdc..baeb51075 100644 --- a/src/Magnum/Shaders/VectorGL.cpp +++ b/src/Magnum/Shaders/VectorGL.cpp @@ -164,7 +164,7 @@ template VectorGL::VectorGL(const Flags flag { #ifndef MAGNUM_TARGET_GLES2 if(flags >= Flag::UniformBuffers) { - _drawOffsetUniform = uniformLocation("drawOffset"); + if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset"); } else #endif { @@ -257,7 +257,7 @@ template VectorGL& VectorGL::set "Shaders::VectorGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); CORRADE_ASSERT(offset < _drawCount, "Shaders::VectorGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); - setUniform(_drawOffsetUniform, offset); + if(_drawCount > 1) setUniform(_drawOffsetUniform, offset); return *this; } diff --git a/src/Magnum/Shaders/VectorGL.h b/src/Magnum/Shaders/VectorGL.h index ad3160304..ef3d2bd33 100644 --- a/src/Magnum/Shaders/VectorGL.h +++ b/src/Magnum/Shaders/VectorGL.h @@ -401,7 +401,9 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * @ref bindDrawBuffer() and @ref bindTextureTransformationBuffer() * should be used for current draw. Expects that * @ref Flag::UniformBuffers is set and @p offset is less than - * @ref drawCount(). Initial value is @cpp 0 @ce. + * @ref drawCount(). Initial value is @cpp 0 @ce, if @ref drawCount() + * is @cpp 1 @ce, the function is a no-op as the shader assumes draw + * offset to be always zero. * * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this * value, which makes each draw submitted via diff --git a/src/Magnum/Shaders/VertexColor.vert b/src/Magnum/Shaders/VertexColor.vert index 53f389cae..d1fbceea0 100644 --- a/src/Magnum/Shaders/VertexColor.vert +++ b/src/Magnum/Shaders/VertexColor.vert @@ -65,6 +65,7 @@ uniform highp mat4 transformationProjectionMatrix /* Uniform buffers */ #else +#if DRAW_COUNT > 1 #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 0) #endif @@ -73,6 +74,9 @@ uniform highp uint drawOffset = 0u #endif ; +#else +#define drawOffset 0u +#endif layout(std140 #ifdef EXPLICIT_BINDING diff --git a/src/Magnum/Shaders/VertexColorGL.cpp b/src/Magnum/Shaders/VertexColorGL.cpp index 7da23cbc4..5a7c5fb9e 100644 --- a/src/Magnum/Shaders/VertexColorGL.cpp +++ b/src/Magnum/Shaders/VertexColorGL.cpp @@ -144,7 +144,7 @@ template VertexColorGL::VertexColorGL(const { #ifndef MAGNUM_TARGET_GLES2 if(flags >= Flag::UniformBuffers) { - _drawOffsetUniform = uniformLocation("drawOffset"); + if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset"); } else #endif { @@ -194,7 +194,7 @@ template VertexColorGL& VertexColorGL 1) setUniform(_drawOffsetUniform, offset); return *this; } diff --git a/src/Magnum/Shaders/VertexColorGL.h b/src/Magnum/Shaders/VertexColorGL.h index c71c9688b..73b23dfa2 100644 --- a/src/Magnum/Shaders/VertexColorGL.h +++ b/src/Magnum/Shaders/VertexColorGL.h @@ -331,7 +331,9 @@ template class MAGNUM_SHADERS_EXPORT VertexColorGL: publ * @ref TransformationProjectionUniform3D buffers bound with * @ref bindTransformationProjectionBuffer() should be used for current * draw. Expects that @ref Flag::UniformBuffers is set and @p offset is - * less than @ref drawCount(). Initial value is @cpp 0 @ce. + * less than @ref drawCount(). Initial value is @cpp 0 @ce, if + * @ref drawCount() is @cpp 1 @ce, the function is a no-op as the + * shader assumes draw offset to be always zero. * * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this * value, which makes each draw submitted via From 78d96946763a4e3bd18a7d7d39ddb2daa0f298d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 8 Jun 2021 13:39:25 +0200 Subject: [PATCH 55/93] Shaders: assume materialId is 0 if MATERIAL_COUNT is 1. Unlike the drawId optimization before, there's no possibility to check this anywhere, so the assumption is just documented. On an Intel 630 this resulted in further significant reduction for the single-draw single-material case, down to 260 from 440 in the previous commit, about a 45% reduction compared to the original 550 ms; multidraw case is still around the 550 there. --- src/Magnum/Shaders/DistanceFieldVector.frag | 4 ++++ src/Magnum/Shaders/DistanceFieldVector.h | 5 +++-- src/Magnum/Shaders/Flat.frag | 4 ++++ src/Magnum/Shaders/Flat.h | 6 ++++-- src/Magnum/Shaders/MeshVisualizer.frag | 4 ++++ src/Magnum/Shaders/MeshVisualizer.h | 5 +++-- src/Magnum/Shaders/Phong.frag | 4 ++++ src/Magnum/Shaders/Phong.h | 5 +++-- src/Magnum/Shaders/Vector.frag | 4 ++++ src/Magnum/Shaders/Vector.h | 5 +++-- 10 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/Magnum/Shaders/DistanceFieldVector.frag b/src/Magnum/Shaders/DistanceFieldVector.frag index 0421f17a1..16385febf 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.frag +++ b/src/Magnum/Shaders/DistanceFieldVector.frag @@ -144,7 +144,11 @@ out lowp vec4 fragmentColor; void main() { #ifdef UNIFORM_BUFFERS + #if MATERIAL_COUNT > 1 mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; + #else + #define materialId 0u + #endif lowp const float smoothness = materials[materialId].material_smoothness; lowp const vec4 color = materials[materialId].color; lowp const vec4 outlineColor = materials[materialId].outlineColor; diff --git a/src/Magnum/Shaders/DistanceFieldVector.h b/src/Magnum/Shaders/DistanceFieldVector.h index 8964793d7..5bd8510ac 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.h +++ b/src/Magnum/Shaders/DistanceFieldVector.h @@ -101,8 +101,9 @@ struct DistanceFieldVectorDrawUniform { * more than one material is supplied or in a multi-draw scenario. Should * be less than the material count passed to the * @ref DistanceFieldVectorGL::DistanceFieldVectorGL(Flags, UnsignedInt, UnsignedInt) - * constructor. Default value is @cpp 0 @ce, meaning the first material - * gets used. + * constructor, if material count is @cpp 1 @ce, this field is assumed to + * be @cpp 0 @ce and isn't even read by the shader. Default value is + * @cpp 0 @ce, meaning the first material gets used. */ /* This field is an UnsignedInt in the shader and materialId is extracted diff --git a/src/Magnum/Shaders/Flat.frag b/src/Magnum/Shaders/Flat.frag index 8f5e5a6d7..dff034e23 100644 --- a/src/Magnum/Shaders/Flat.frag +++ b/src/Magnum/Shaders/Flat.frag @@ -176,7 +176,11 @@ void main() { #ifdef OBJECT_ID highp const uint objectId = draws[drawId].draw_objectId; #endif + #if MATERIAL_COUNT > 1 mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; + #else + #define materialId 0u + #endif lowp const vec4 color = materials[materialId].color; #ifdef ALPHA_MASK lowp const float alphaMask = materials[materialId].material_alphaMask; diff --git a/src/Magnum/Shaders/Flat.h b/src/Magnum/Shaders/Flat.h index 4a894c173..8e22d887d 100644 --- a/src/Magnum/Shaders/Flat.h +++ b/src/Magnum/Shaders/Flat.h @@ -108,8 +108,10 @@ struct FlatDrawUniform { * References a particular material from a @ref FlatMaterialUniform array. * Useful when an UBO with more than one material is supplied or in a * multi-draw scenario. Should be less than the material count passed to - * the @ref FlatGL::FlatGL(Flags, UnsignedInt, UnsignedInt) constructor. - * Default value is @cpp 0 @ce, meaning the first material gets used. + * the @ref FlatGL::FlatGL(Flags, UnsignedInt, UnsignedInt) constructor, if + * material count is @cpp 1 @ce, this field is assumed to be @cpp 0 @ce and + * isn't even read by the shader. Default value is @cpp 0 @ce, meaning the + * first material gets used. */ /* This field is an UnsignedInt in the shader and materialId is extracted diff --git a/src/Magnum/Shaders/MeshVisualizer.frag b/src/Magnum/Shaders/MeshVisualizer.frag index 20bb89ec7..d1e467052 100644 --- a/src/Magnum/Shaders/MeshVisualizer.frag +++ b/src/Magnum/Shaders/MeshVisualizer.frag @@ -224,7 +224,11 @@ out lowp vec4 fragmentColor; void main() { #ifdef UNIFORM_BUFFERS + #if MATERIAL_COUNT > 1 mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; + #else + #define materialId 0u + #endif #if (defined(WIREFRAME_RENDERING) || defined(INSTANCED_OBJECT_ID) || defined(VERTEX_ID) || defined(PRIMITIVE_ID) || defined(PRIMITIVE_ID_FROM_VERTEX_ID)) && !defined(TBN_DIRECTION) lowp const vec4 color = materials[materialId].color; lowp const vec4 wireframeColor = materials[materialId].wireframeColor; diff --git a/src/Magnum/Shaders/MeshVisualizer.h b/src/Magnum/Shaders/MeshVisualizer.h index 2e4038412..9b0a540c4 100644 --- a/src/Magnum/Shaders/MeshVisualizer.h +++ b/src/Magnum/Shaders/MeshVisualizer.h @@ -100,8 +100,9 @@ struct MeshVisualizerDrawUniform2D { * more than one material is supplied or in a multi-draw scenario. Should * be less than the material count passed to the @ref MeshVisualizerGL2D::MeshVisualizerGL2D(Flags, UnsignedInt, UnsignedInt) * / @ref MeshVisualizerGL3D::MeshVisualizerGL3D(Flags, UnsignedInt, UnsignedInt) - * constructor. Default value is @cpp 0 @ce, meaning the first material - * gets used. + * constructor, if material count is @cpp 1 @ce, this field is assumed to + * be @cpp 0 @ce and isn't even read by the shader. Default value is + * @cpp 0 @ce, meaning the first material gets used. */ /* This field is an UnsignedInt in the shader and materialId is extracted diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index 57ab5b2d5..ccfb8371a 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -339,7 +339,11 @@ void main() { #ifdef OBJECT_ID highp const uint objectId = draws[drawId].draw_objectId; #endif + #if MATERIAL_COUNT > 1 mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; + #else + #define materialId 0u + #endif lowp const vec4 ambientColor = materials[materialId].ambientColor; #if LIGHT_COUNT lowp const vec4 diffuseColor = materials[materialId].diffuseColor; diff --git a/src/Magnum/Shaders/Phong.h b/src/Magnum/Shaders/Phong.h index c315e171d..8ac861067 100644 --- a/src/Magnum/Shaders/Phong.h +++ b/src/Magnum/Shaders/Phong.h @@ -141,8 +141,9 @@ struct PhongDrawUniform { * Useful when a UBO with more than one material is supplied or in a * multi-draw scenario. Should be less than the material count passed to * the @ref PhongGL::PhongGL(Flags, UnsignedInt, UnsignedInt, UnsignedInt) - * constructor. Default value is @cpp 0 @ce, meaning the first material - * gets used. + * constructor, if material count is @cpp 1 @ce, this field is assumed to + * be @cpp 0 @ce and isn't even read by the shader. Default value is + * @cpp 0 @ce, meaning the first material gets used. */ /* This field is an UnsignedInt in the shader and materialId is extracted diff --git a/src/Magnum/Shaders/Vector.frag b/src/Magnum/Shaders/Vector.frag index 1b78de18f..50477a9b2 100644 --- a/src/Magnum/Shaders/Vector.frag +++ b/src/Magnum/Shaders/Vector.frag @@ -123,7 +123,11 @@ out lowp vec4 fragmentColor; void main() { #ifdef UNIFORM_BUFFERS + #if MATERIAL_COUNT > 1 mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; + #else + #define materialId 0u + #endif lowp const vec4 color = materials[materialId].color; lowp const vec4 backgroundColor = materials[materialId].backgroundColor; #endif diff --git a/src/Magnum/Shaders/Vector.h b/src/Magnum/Shaders/Vector.h index 4d6b44675..daaf3d70a 100644 --- a/src/Magnum/Shaders/Vector.h +++ b/src/Magnum/Shaders/Vector.h @@ -96,8 +96,9 @@ struct VectorDrawUniform { * array. Useful when an UBO with more than one material is supplied or in * a multi-draw scenario. Should be less than the material count passed to * the @ref VectorGL::VectorGL(Flags, UnsignedInt, UnsignedInt) - * constructor. Default value is @cpp 0 @ce, meaning the first material - * gets used. + * constructor, if material count is @cpp 1 @ce, this field is assumed to + * be @cpp 0 @ce and isn't even read by the shader. Default value is + * @cpp 0 @ce, meaning the first material gets used. */ /* This field is an UnsignedInt in the shader and materialId is extracted From fd3bd7e7377e04b407ab4660052e297280dcd2d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 8 Jun 2021 11:32:04 +0200 Subject: [PATCH 56/93] Shaders: ANGLE, for fucks sake! --- src/Magnum/Shaders/Flat.vert | 20 ++++++++++---------- src/Magnum/Shaders/MeshVisualizer.frag | 3 ++- src/Magnum/Shaders/MeshVisualizer.geom | 3 ++- src/Magnum/Shaders/MeshVisualizer.vert | 11 +++++++---- src/Magnum/Shaders/Phong.frag | 3 ++- src/Magnum/Shaders/Phong.vert | 17 +++++++++++++++-- src/Magnum/Shaders/Vector.vert | 20 ++++++++++---------- src/Magnum/Shaders/VertexColor.vert | 20 ++++++++++---------- 8 files changed, 58 insertions(+), 39 deletions(-) diff --git a/src/Magnum/Shaders/Flat.vert b/src/Magnum/Shaders/Flat.vert index dfae3fcf1..eb71c6730 100644 --- a/src/Magnum/Shaders/Flat.vert +++ b/src/Magnum/Shaders/Flat.vert @@ -112,7 +112,9 @@ layout(std140 ) uniform TransformationProjection { highp #ifdef TWO_DIMENSIONS - mat3 + /* Can't be a mat3 because of ANGLE, see DrawUniform in Phong.vert for + details */ + mat3x4 #elif defined(THREE_DIMENSIONS) mat4 #else @@ -237,15 +239,13 @@ void main() { #define drawId drawOffset #endif - highp const - #ifdef TWO_DIMENSIONS - mat3 - #elif defined(THREE_DIMENSIONS) - mat4 - #else - #error - #endif - transformationProjectionMatrix = transformationProjectionMatrices[drawId]; + #ifdef TWO_DIMENSIONS + highp const mat3 transformationProjectionMatrix = mat3(transformationProjectionMatrices[drawId]); + #elif defined(THREE_DIMENSIONS) + highp const mat4 transformationProjectionMatrix = transformationProjectionMatrices[drawId]; + #else + #error + #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 diff --git a/src/Magnum/Shaders/MeshVisualizer.frag b/src/Magnum/Shaders/MeshVisualizer.frag index d1e467052..db16422a8 100644 --- a/src/Magnum/Shaders/MeshVisualizer.frag +++ b/src/Magnum/Shaders/MeshVisualizer.frag @@ -131,7 +131,8 @@ uniform highp uint drawOffset always before any code. */ struct DrawUniform { #ifdef THREE_DIMENSIONS - highp mat3 normalMatrix; /* actually mat3x4 */ + /* Can't be a mat3 because of ANGLE, see Phong.vert for details */ + highp mat3x4 normalMatrix; #elif !defined(TWO_DIMENSIONS) #error #endif diff --git a/src/Magnum/Shaders/MeshVisualizer.geom b/src/Magnum/Shaders/MeshVisualizer.geom index 3cfe14402..ac662e2ad 100644 --- a/src/Magnum/Shaders/MeshVisualizer.geom +++ b/src/Magnum/Shaders/MeshVisualizer.geom @@ -103,7 +103,8 @@ uniform highp uint drawOffset always before any code. */ struct DrawUniform { #ifdef THREE_DIMENSIONS - highp mat3 normalMatrix; /* actually mat3x4 */ + /* Can't be a mat3 because of ANGLE, see Phong.vert for details */ + highp mat3x4 normalMatrix; #elif !defined(TWO_DIMENSIONS) #error #endif diff --git a/src/Magnum/Shaders/MeshVisualizer.vert b/src/Magnum/Shaders/MeshVisualizer.vert index c0e514e75..43359b178 100644 --- a/src/Magnum/Shaders/MeshVisualizer.vert +++ b/src/Magnum/Shaders/MeshVisualizer.vert @@ -132,7 +132,9 @@ layout(std140 , binding = 1 #endif ) uniform TransformationProjection { - highp mat3 transformationProjectionMatrices[DRAW_COUNT]; + /* Can't be a mat3 because of ANGLE, see DrawUniform in Phong.vert for + details */ + highp mat3x4 transformationProjectionMatrices[DRAW_COUNT]; }; #elif defined(THREE_DIMENSIONS) layout(std140 @@ -159,7 +161,8 @@ layout(std140 always before any code. */ struct DrawUniform { #ifdef THREE_DIMENSIONS - highp mat3 normalMatrix; /* actually mat3x4 */ + /* Can't be a mat3 because of ANGLE, see Phong.vert for details */ + highp mat3x4 normalMatrix; #elif !defined(TWO_DIMENSIONS) #error #endif @@ -322,14 +325,14 @@ void main() { #endif #ifdef TWO_DIMENSIONS - highp const mat3 transformationProjectionMatrix = transformationProjectionMatrices[drawId]; + highp const mat3 transformationProjectionMatrix = mat3(transformationProjectionMatrices[drawId]); #elif defined(THREE_DIMENSIONS) highp const mat4 transformationMatrix = transformationMatrices[drawId]; #else #error #endif #if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(BITANGENT_FROM_TANGENT_DIRECTION) || defined(NORMAL_DIRECTION) - mediump const mat3 normalMatrix = draws[drawId].normalMatrix; + mediump const mat3 normalMatrix = mat3(draws[drawId].normalMatrix); #endif mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; lowp float colorMapOffset = materials[materialId].material_colorMapOffset; diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index ccfb8371a..42e05b121 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -167,7 +167,8 @@ uniform highp uint drawOffset /* Keep in sync with Phong.vert. Can't "outsource" to a common file because the #extension directive needs to be always before any code. */ struct DrawUniform { - mediump mat3 normalMatrix; /* actually mat3x4 */ + /* Can't be a mat3 because of ANGLE, see Phong.vert for details */ + mediump mat3x4 normalMatrix; highp uvec4 materialIdReservedObjectIdLightOffsetLightCount; #define draw_materialIdReserved materialIdReservedObjectIdLightOffsetLightCount.x #define draw_objectId materialIdReservedObjectIdLightOffsetLightCount.y diff --git a/src/Magnum/Shaders/Phong.vert b/src/Magnum/Shaders/Phong.vert index 521c55389..2769fa980 100644 --- a/src/Magnum/Shaders/Phong.vert +++ b/src/Magnum/Shaders/Phong.vert @@ -130,7 +130,20 @@ uniform highp uint drawOffset /* Keep in sync with Phong.frag. Can't "outsource" to a common file because the #extension directive needs to be always before any code. */ struct DrawUniform { - mediump mat3 normalMatrix; /* actually mat3x4 */ + /* Of all drivers, I made the crucial mistake of expecting ANGLE to have + non-broken uniform packing. Of course everything including random phone + drivers worked, except ANGLE with a D3D backend, which blew up when + seeing `mat3` here. With all the coding guidelines, rules and automated + bullying from Clang Tidy in place over at Google, it seems the false + sense of security is so strong that they don't even bother testing what + they wrote. Or, how to code, the Google way: + + 1. Thoroughly document the packing rules and how the translation of + every GLSL type to the D3D equivalent is performed: + https://chromium.googlesource.com/angle/angle/+/refs/heads/main/src/libANGLE/renderer/d3d/d3d11/UniformBlockToStructuredBufferTranslation.md#std140-limitation + 2. Forget to actually implement and test the damn thing. + */ + mediump mat3x4 normalMatrix; highp uvec4 materialIdReservedObjectIdLightOffsetLightCount; #define draw_materialIdReserved materialIdReservedObjectIdLightOffsetLightCount.x #define draw_objectId materialIdReservedObjectIdLightOffsetLightCount.y @@ -336,7 +349,7 @@ void main() { highp const mat4 transformationMatrix = transformationMatrices[drawId]; #if LIGHT_COUNT - mediump const mat3 normalMatrix = draws[drawId].normalMatrix; + mediump const mat3 normalMatrix = mat3(draws[drawId].normalMatrix); #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); diff --git a/src/Magnum/Shaders/Vector.vert b/src/Magnum/Shaders/Vector.vert index f92fe714b..c0acfe667 100644 --- a/src/Magnum/Shaders/Vector.vert +++ b/src/Magnum/Shaders/Vector.vert @@ -96,7 +96,9 @@ layout(std140 ) uniform TransformationProjection { highp #ifdef TWO_DIMENSIONS - mat3 + /* Can't be a mat3 because of ANGLE, see DrawUniform in Phong.vert for + details */ + mat3x4 #elif defined(THREE_DIMENSIONS) mat4 #else @@ -162,15 +164,13 @@ void main() { #define drawId drawOffset #endif - highp const - #ifdef TWO_DIMENSIONS - mat3 - #elif defined(THREE_DIMENSIONS) - mat4 - #else - #error - #endif - transformationProjectionMatrix = transformationProjectionMatrices[drawId]; + #ifdef TWO_DIMENSIONS + highp const mat3 transformationProjectionMatrix = mat3(transformationProjectionMatrices[drawId]); + #elif defined(THREE_DIMENSIONS) + highp const mat4 transformationProjectionMatrix = transformationProjectionMatrices[drawId]; + #else + #error + #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); #endif diff --git a/src/Magnum/Shaders/VertexColor.vert b/src/Magnum/Shaders/VertexColor.vert index d1fbceea0..9c16869b6 100644 --- a/src/Magnum/Shaders/VertexColor.vert +++ b/src/Magnum/Shaders/VertexColor.vert @@ -85,7 +85,9 @@ layout(std140 ) uniform TransformationProjection { highp #ifdef TWO_DIMENSIONS - mat3 + /* Can't be a mat3 because of ANGLE, see DrawUniform in Phong.vert for + details */ + mat3x4 #elif defined(THREE_DIMENSIONS) mat4 #else @@ -131,15 +133,13 @@ void main() { #define drawId drawOffset #endif - highp const - #ifdef TWO_DIMENSIONS - mat3 - #elif defined(THREE_DIMENSIONS) - mat4 - #else - #error - #endif - transformationProjectionMatrix = transformationProjectionMatrices[drawId]; + #ifdef TWO_DIMENSIONS + highp const mat3 transformationProjectionMatrix = mat3(transformationProjectionMatrices[drawId]); + #elif defined(THREE_DIMENSIONS) + highp const mat4 transformationProjectionMatrix = transformationProjectionMatrices[drawId]; + #else + #error + #endif #endif #ifdef TWO_DIMENSIONS From 35aba9aa8ec7d68350fa6262d4c3494cd7ebe9eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 8 Jun 2021 16:21:55 +0200 Subject: [PATCH 57/93] Shaders: SwiftShader, I DON'T LIKE YOU. "Luckily", thanks to the DRAW_COUNT=1 and MATERIAL_COUNT=1 optimizations not everything blows up, so i don't need to skip absolutely everything, unfortunately Phong lights are affected by this insane crapfest as well so basically nothing from Phong UBO support is tested there. FFS. --- .../Test/DistanceFieldVectorGLTest.cpp | 10 ++++ src/Magnum/Shaders/Test/FlatGLTest.cpp | 10 ++++ .../Shaders/Test/MeshVisualizerGLTest.cpp | 10 ++++ src/Magnum/Shaders/Test/PhongGLTest.cpp | 60 +++++++++++++++++++ src/Magnum/Shaders/Test/VectorGLTest.cpp | 10 ++++ src/Magnum/Shaders/Test/VertexColorGLTest.cpp | 10 ++++ 6 files changed, 110 insertions(+) diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp index 518286fcb..70ecf2053 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp @@ -1071,6 +1071,11 @@ void DistanceFieldVectorGLTest::renderMulti2D() { #endif } + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -1267,6 +1272,11 @@ void DistanceFieldVectorGLTest::renderMulti3D() { #endif } + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index cfb25c0ee..b5a8beedc 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -3015,6 +3015,11 @@ void FlatGLTest::renderMulti2D() { #endif } + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif + FlatGL2D shader{FlatGL2D::Flag::UniformBuffers|FlatGL2D::Flag::ObjectId|data.flags, data.materialCount, data.drawCount}; GL::Texture2D texture{NoCreate}; @@ -3307,6 +3312,11 @@ void FlatGLTest::renderMulti3D() { #endif } + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif + FlatGL3D shader{FlatGL3D::Flag::UniformBuffers|FlatGL3D::Flag::ObjectId|data.flags, data.materialCount, data.drawCount}; GL::Texture2D texture{NoCreate}; diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index 9c13ebba6..eafd8f0f7 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -3243,6 +3243,11 @@ void MeshVisualizerGLTest::renderMulti2D() { #endif } + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif + /* Circle is a fan, plane is a strip, make it indexed first */ Trade::MeshData circleData = MeshTools::generateIndices(Primitives::circle2DSolid(8)); Trade::MeshData squareData = MeshTools::generateIndices(Primitives::squareSolid()); @@ -3436,6 +3441,11 @@ void MeshVisualizerGLTest::renderMulti3D() { #endif } + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif + Trade::MeshData sphereData = MeshTools::interleave(Primitives::icosphereSolid(0), { /* The icosphere doesn't have tangents and we don't use them, but concatenate() will ignore the tangents of others if the first mesh diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index a969e39b0..62208759d 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -1474,6 +1474,11 @@ template void PhongGLTest::renderDefaults() { if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed (light) arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif } #endif @@ -1545,6 +1550,11 @@ template void PhongGLTest::renderColored() { if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed (light) arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif } #endif @@ -1655,6 +1665,11 @@ template void PhongGLTest::renderSinglePixelTextured() { if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed (light) arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif } #endif @@ -1837,6 +1852,11 @@ template void PhongGLTest::renderTextured() { if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed (light) arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif } #endif @@ -2065,6 +2085,11 @@ template void PhongGLTest::renderTexturedNormal() { if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed (light) arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif } #endif @@ -2253,6 +2278,11 @@ template void PhongGLTest::renderVertexColor() { if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed (light) arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif } else #endif { @@ -2376,6 +2406,11 @@ template void PhongGLTest::renderShininess() { if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed (light) arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif } #endif @@ -2523,6 +2558,11 @@ template void PhongGLTest::renderAlpha() { if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed (light) arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif } #endif @@ -2703,6 +2743,11 @@ template void PhongGLTest::renderObjectId() { if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed (light) arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif } #endif @@ -2814,6 +2859,11 @@ template void PhongGLTest::renderLights() { if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed (light) arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif } #endif @@ -3173,6 +3223,11 @@ template void PhongGLTest::renderInstanced() { if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); #endif + + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed (light) arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif } #endif @@ -3553,6 +3608,11 @@ void PhongGLTest::renderMulti() { #endif } + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif + PhongGL shader{PhongGL::Flag::UniformBuffers|PhongGL::Flag::ObjectId|data.flags, data.lightCount, data.materialCount, data.drawCount}; GL::Texture2D diffuse{NoCreate}; diff --git a/src/Magnum/Shaders/Test/VectorGLTest.cpp b/src/Magnum/Shaders/Test/VectorGLTest.cpp index ce9a77d21..0e7d7a548 100644 --- a/src/Magnum/Shaders/Test/VectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VectorGLTest.cpp @@ -1036,6 +1036,11 @@ void VectorGLTest::renderMulti2D() { #endif } + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); @@ -1232,6 +1237,11 @@ void VectorGLTest::renderMulti3D() { #endif } + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); diff --git a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp index bff07e338..9bb6392fa 100644 --- a/src/Magnum/Shaders/Test/VertexColorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VertexColorGLTest.cpp @@ -793,6 +793,11 @@ void VertexColorGLTest::renderMulti2D() { #endif } + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif + /* Circle is a fan, plane is a strip, make it indexed first */ Trade::MeshData circleData = MeshTools::generateIndices(Primitives::circle2DSolid(32)); Trade::MeshData squareData = MeshTools::generateIndices(Primitives::squareSolid()); @@ -916,6 +921,11 @@ void VertexColorGLTest::renderMulti3D() { #endif } + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif + Trade::MeshData sphereData = Primitives::uvSphereSolid(16, 32); /* Plane is a strip, make it indexed first */ Trade::MeshData planeData = MeshTools::generateIndices(Primitives::planeSolid()); From df75ab926a49e34153e4e160c029c1ff7e92a9f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 20 May 2021 17:24:18 +0200 Subject: [PATCH 58/93] Shaders: calculate Phong light directions in the fragment shader. Hm, I wonder why I did it this way, the operation isn't really heavy to benefit from the hardware interpolators, and with a lot of lights we'd hit the maximum output count in the vertex shader. With classic uniforms this seems to be ~5% faster on both Intel and AMD cards. With UBOs this is ~15% (!) faster on AMD (I guess because the constant UBO access overhead is moved to just one stage instead of both?) but slower on Intel (of course, sigh... I assume due to UBO reads being slow and so when done for every fragment instead of every vertex it costs more?). Since this benefits the *real* GPUs while the card that was already awful is more awful I don't think that's a big deal. This change stays. --- src/Magnum/Shaders/Phong.frag | 32 ++++++++++---- src/Magnum/Shaders/Phong.vert | 76 +++++----------------------------- src/Magnum/Shaders/PhongGL.cpp | 51 ++++++++++------------- 3 files changed, 56 insertions(+), 103 deletions(-) diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index 42e05b121..01060818f 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -113,6 +113,16 @@ uniform highp uint objectId; /* defaults to zero */ #endif #if LIGHT_COUNT +/* Needs to be last because it uses locations 12 to 12 + LIGHT_COUNT - 1 */ +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 12) +#endif +uniform highp vec4 lightPositions[LIGHT_COUNT] + #ifndef GL_ES + = vec4[](LIGHT_POSITION_INITIALIZER) + #endif + ; + /* 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. @@ -202,8 +212,7 @@ layout(std140 MaterialUniform materials[MATERIAL_COUNT]; }; -/* Keep in sync with Phong.vert. Can't "outsource" to a common file because - the #extension directive needs to be always before any code. */ +#if LIGHT_COUNT struct LightUniform { highp vec4 position; lowp vec3 colorReserved; @@ -214,7 +223,6 @@ struct LightUniform { #define light_range rangeReservedReservedReserved.x }; -#if LIGHT_COUNT layout(std140 #ifdef EXPLICIT_BINDING , binding = 5 @@ -293,8 +301,7 @@ in mediump vec3 transformedTangent; in mediump vec3 transformedBitangent; #endif #endif -in highp vec4 lightDirections[LIGHT_COUNT]; -in highp vec3 cameraDirection; +in highp vec3 transformedPosition; #endif #if defined(AMBIENT_TEXTURE) || defined(DIFFUSE_TEXTURE) || defined(SPECULAR_TEXTURE) || defined(NORMAL_TEXTURE) @@ -441,17 +448,26 @@ void main() { #endif ; + highp const vec4 lightPosition = + #ifndef UNIFORM_BUFFERS + lightPositions[i] + #else + lights[lightOffset + i].position + #endif + ; + highp const vec4 lightDirection = vec4(lightPosition.xyz - transformedPosition*lightPosition.w, lightPosition.w); + /* Attenuation. Directional lights have the .w component set to 0, use that to make the distance zero -- which will then ensure the attenuation is always 1.0 */ - highp float dist = length(lightDirections[i].xyz)*lightDirections[i].w; + highp float dist = length(lightDirection.xyz)*lightDirection.w; /* If range is 0 for whatever reason, clamp it to a small value to avoid a NaN when dist is 0 as well (which is the case for directional lights). */ highp float attenuation = clamp(1.0 - pow(dist/max(lightRange, 0.0001), 4.0), 0.0, 1.0); attenuation = attenuation*attenuation/(1.0 + dist*dist); - highp vec3 normalizedLightDirection = normalize(lightDirections[i].xyz); + highp vec3 normalizedLightDirection = normalize(lightDirection.xyz); lowp float intensity = max(0.0, dot(normalizedTransformedNormal, normalizedLightDirection))*attenuation; fragmentColor += vec4(finalDiffuseColor.rgb*lightColor*intensity, finalDiffuseColor.a/float( #ifndef UNIFORM_BUFFERS @@ -465,7 +481,7 @@ void main() { if(intensity > 0.001) { highp vec3 reflection = reflect(-normalizedLightDirection, normalizedTransformedNormal); /* Use attenuation for the specularity as well */ - mediump float specularity = clamp(pow(max(0.0, dot(normalize(cameraDirection), reflection)), shininess), 0.0, 1.0)*attenuation; + mediump float specularity = clamp(pow(max(0.0, dot(normalize(-transformedPosition), reflection)), shininess), 0.0, 1.0)*attenuation; fragmentColor += vec4(finalSpecularColor.rgb*lightSpecularColor.rgb*specularity, finalSpecularColor.a); } } diff --git a/src/Magnum/Shaders/Phong.vert b/src/Magnum/Shaders/Phong.vert index 2769fa980..4578d03e2 100644 --- a/src/Magnum/Shaders/Phong.vert +++ b/src/Magnum/Shaders/Phong.vert @@ -69,7 +69,7 @@ uniform highp mat4 projectionMatrix #endif ; -#if LIGHT_COUNT +#ifdef HAS_LIGHTS #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 2) #endif @@ -99,18 +99,6 @@ layout(location = 4) 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 = 12) -#endif -uniform highp vec4 lightPositions[LIGHT_COUNT] - #ifndef GL_ES - = vec4[](LIGHT_POSITION_INITIALIZER) - #endif - ; -#endif - /* Uniform buffers */ #else @@ -191,28 +179,6 @@ layout(std140 TextureTransformationUniform textureTransformations[DRAW_COUNT]; }; #endif - -#if LIGHT_COUNT -/* Keep in sync with Phong.frag. Can't "outsource" to a common file because - the #extension directive needs to be always before any code. */ -struct LightUniform { - highp vec4 position; - lowp vec3 colorReserved; - #define light_color colorReserved.xyz - lowp vec4 specularColorReserved; - #define light_specularColor specularColorReserved.xyz - lowp vec4 rangeReservedReservedReserved; - #define light_range rangeReservedReservedReserved.x -}; - -layout(std140 - #ifdef EXPLICIT_BINDING - , binding = 5 - #endif -) uniform Light { - LightUniform lights[LIGHT_COUNT]; -}; -#endif #endif /* Inputs */ @@ -222,7 +188,7 @@ layout(location = POSITION_ATTRIBUTE_LOCATION) #endif in highp vec4 position; -#if LIGHT_COUNT +#ifdef HAS_LIGHTS #ifdef EXPLICIT_ATTRIB_LOCATION layout(location = NORMAL_ATTRIBUTE_LOCATION) #endif @@ -315,7 +281,7 @@ out lowp vec4 interpolatedVertexColor; flat out highp uint interpolatedInstanceObjectId; #endif -#if LIGHT_COUNT +#ifdef HAS_LIGHTS out mediump vec3 transformedNormal; #ifdef NORMAL_TEXTURE #ifndef BITANGENT @@ -325,8 +291,7 @@ out mediump vec3 transformedTangent; out mediump vec3 transformedBitangent; #endif #endif -out highp vec4 lightDirections[LIGHT_COUNT]; -out highp vec3 cameraDirection; +out highp vec3 transformedPosition; #endif #ifdef MULTI_DRAW @@ -348,7 +313,7 @@ void main() { #endif highp const mat4 transformationMatrix = transformationMatrices[drawId]; - #if LIGHT_COUNT + #ifdef HAS_LIGHTS mediump const mat3 normalMatrix = mat3(draws[drawId].normalMatrix); #endif #ifdef TEXTURE_TRANSFORMATION @@ -357,9 +322,6 @@ void main() { highp const uint textureLayer = floatBitsToUint(textureTransformations[drawId].textureTransformation_layer); #endif #endif - #if LIGHT_COUNT - mediump const uint lightOffset = draws[drawId].draw_lightOffset; - #endif #endif /* Transformed vertex position */ @@ -368,9 +330,12 @@ void main() { instancedTransformationMatrix* #endif position; - highp vec3 transformedPosition = transformedPosition4.xyz/transformedPosition4.w; + #ifndef HAS_LIGHTS + highp vec3 + #endif + transformedPosition = transformedPosition4.xyz/transformedPosition4.w; - #if LIGHT_COUNT + #ifdef HAS_LIGHTS /* Transformed normal and tangent vector */ transformedNormal = normalMatrix* #ifdef INSTANCED_TRANSFORMATION @@ -397,27 +362,6 @@ void main() { bitangent; #endif #endif - - /* Direction to the light. Directional lights have the last component set - to 0, which gets used to ignore the transformed position. */ - #ifndef UNIFORM_BUFFERS - for(int i = 0; i < LIGHT_COUNT; ++i) - #else - for(uint i = 0u, actualLightCount = min(uint(LIGHT_COUNT), draws[drawId].draw_lightCount); i < actualLightCount; ++i) - #endif - { - highp const vec4 lightPosition = - #ifndef UNIFORM_BUFFERS - lightPositions[i] - #else - lights[lightOffset + i].position - #endif - ; - lightDirections[i] = vec4(lightPosition.xyz - transformedPosition*lightPosition.w, lightPosition.w); - } - - /* Direction to the camera */ - cameraDirection = -transformedPosition; #endif /* Transform the position */ diff --git a/src/Magnum/Shaders/PhongGL.cpp b/src/Magnum/Shaders/PhongGL.cpp index 0190c050e..586f15cbe 100644 --- a/src/Magnum/Shaders/PhongGL.cpp +++ b/src/Magnum/Shaders/PhongGL.cpp @@ -153,7 +153,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment); #ifndef MAGNUM_TARGET_GLES - std::string lightInitializerVertex, lightInitializerFragment; + std::string lightInitializer; if(!(flags >= Flag::UniformBuffers) && lightCount) { using namespace Containers::Literals; @@ -167,39 +167,37 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount constexpr Containers::StringView lightColorInitializerItem = "vec3(1.0), "_s; constexpr Containers::StringView lightRangeInitializerItem = "1.0/0.0, "_s; - lightInitializerVertex.reserve( + lightInitializer.reserve( lightPositionInitializerPreamble.size() + - lightCount*(lightPositionInitializerItem.size())); + lightColorInitializerPreamble.size() + + lightRangeInitializerPreamble.size() + + lightCount*(lightPositionInitializerItem.size() + + lightColorInitializerItem.size() + + lightRangeInitializerItem.size())); - lightInitializerVertex.append(lightPositionInitializerPreamble); + lightInitializer.append(lightPositionInitializerPreamble); for(std::size_t i = 0; i != lightCount; ++i) - lightInitializerVertex.append(lightPositionInitializerItem); + lightInitializer.append(lightPositionInitializerItem); /* Drop the last comma and add a newline at the end */ - lightInitializerVertex[lightInitializerVertex.size() - 2] = '\n'; - lightInitializerVertex.resize(lightInitializerVertex.size() - 1); - - lightInitializerFragment.reserve( - lightColorInitializerPreamble.size() + - lightRangeInitializerPreamble.size() + - lightCount*(lightColorInitializerItem.size() + - lightRangeInitializerItem.size())); + lightInitializer[lightInitializer.size() - 2] = '\n'; + lightInitializer.resize(lightInitializer.size() - 1); - lightInitializerFragment.append(lightColorInitializerPreamble); + lightInitializer.append(lightColorInitializerPreamble); for(std::size_t i = 0; i != lightCount; ++i) - lightInitializerFragment.append(lightColorInitializerItem); + lightInitializer.append(lightColorInitializerItem); /* Drop the last comma and add a newline at the end */ - lightInitializerFragment[lightInitializerFragment.size() - 2] = '\n'; - lightInitializerFragment.resize(lightInitializerFragment.size() - 1); + lightInitializer[lightInitializer.size() - 2] = '\n'; + lightInitializer.resize(lightInitializer.size() - 1); - lightInitializerFragment.append(lightRangeInitializerPreamble); + lightInitializer.append(lightRangeInitializerPreamble); for(std::size_t i = 0; i != lightCount; ++i) - lightInitializerFragment.append(lightRangeInitializerItem); + lightInitializer.append(lightRangeInitializerItem); /* Drop the last comma and add a newline at the end */ - lightInitializerFragment[lightInitializerFragment.size() - 2] = '\n'; - lightInitializerFragment.resize(lightInitializerFragment.size() - 1); + lightInitializer[lightInitializer.size() - 2] = '\n'; + lightInitializer.resize(lightInitializer.size() - 1); } #endif @@ -211,7 +209,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount #ifndef MAGNUM_TARGET_GLES2 .addSource(flags & Flag::TextureArrays ? "#define TEXTURE_ARRAYS\n" : "") #endif - .addSource(Utility::formatString("#define LIGHT_COUNT {}\n", lightCount)) + .addSource(lightCount ? "#define HAS_LIGHTS\n" : "") #ifndef MAGNUM_TARGET_GLES2 .addSource(flags >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") #endif @@ -221,17 +219,12 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount if(flags >= Flag::UniformBuffers) { vert.addSource(Utility::formatString( "#define UNIFORM_BUFFERS\n" - "#define DRAW_COUNT {}\n" - "#define LIGHT_COUNT {}\n", + "#define DRAW_COUNT {}\n", drawCount, lightCount)); vert.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); } #endif - #ifndef MAGNUM_TARGET_GLES - if(!(flags >= Flag::UniformBuffers) && lightCount) - vert.addSource(std::move(lightInitializerVertex)); - #endif vert.addSource(rs.get("generic.glsl")) .addSource(rs.get("Phong.vert")); frag.addSource(flags & Flag::AmbientTexture ? "#define AMBIENT_TEXTURE\n" : "") @@ -275,7 +268,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount } #ifndef MAGNUM_TARGET_GLES if(!(flags >= Flag::UniformBuffers) && lightCount) - frag.addSource(std::move(lightInitializerFragment)); + frag.addSource(std::move(lightInitializer)); #endif frag.addSource(rs.get("generic.glsl")) .addSource(rs.get("Phong.frag")); From bda01bb3a5ba3e3d6395cb9e0d456470968ee7be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 9 Jun 2021 12:36:35 +0200 Subject: [PATCH 59/93] Shaders: benchmark Phong with 64 lights but just 5 used. Should be roughly the same perf as one with 5 set statically. --- src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp index e9ae264d3..5ec5d80f3 100644 --- a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp +++ b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp @@ -197,6 +197,7 @@ const struct { {"UBO single, ADS texture arrays + transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::TextureTransformation, 1, 1, 1}, {"UBO multi, one light", PhongGL::Flag::UniformBuffers, 1, 32, 128}, {"multidraw, one light", PhongGL::Flag::MultiDraw, 1, 32, 128}, + {"multidraw, 64 lights, five used", PhongGL::Flag::MultiDraw, 64, 32, 128}, #endif }; @@ -694,7 +695,10 @@ void ShadersGLBenchmark::phong() { ProjectionUniform3D{} }}; transformationUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; - drawUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; + Containers::Array drawData{data.drawCount}; + drawData[0].lightCount = 5; /* Cap at 5 lights, even if more is set */ + drawUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, drawData}; + Containers::Array materialData{data.materialCount}; materialData[0] /* White ambient so we always have a white output */ From 000c332eee12fbd500a99e5edd75de5968b06e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 9 Jun 2021 12:58:33 +0200 Subject: [PATCH 60/93] Shaders: this was a stupid thing to do. Doesn't actually seem to have any measurable impact (on Intel), so I guess the compiler can realize this as well or it was really unimpactful. --- src/Magnum/Shaders/Phong.frag | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index 01060818f..79792e3f9 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -469,13 +469,7 @@ void main() { highp vec3 normalizedLightDirection = normalize(lightDirection.xyz); lowp float intensity = max(0.0, dot(normalizedTransformedNormal, normalizedLightDirection))*attenuation; - fragmentColor += vec4(finalDiffuseColor.rgb*lightColor*intensity, finalDiffuseColor.a/float( - #ifndef UNIFORM_BUFFERS - LIGHT_COUNT - #else - actualLightCount - #endif - )); + fragmentColor.rgb += finalDiffuseColor.rgb*lightColor*intensity; /* Add specular color, if needed */ if(intensity > 0.001) { @@ -485,6 +479,8 @@ void main() { fragmentColor += vec4(finalSpecularColor.rgb*lightSpecularColor.rgb*specularity, finalSpecularColor.a); } } + + fragmentColor.a += finalDiffuseColor.a; #endif #ifdef ALPHA_MASK From 0b7c7f824928c508578e0c5222cb8c37d143b23e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 9 Jun 2021 13:10:30 +0200 Subject: [PATCH 61/93] Shaders: hoist camera calculation calculation out of the loop. While (of course) having zero effect on a single-light scenario, with five lights it saves about 10% in the classic uniform case (on Intel). Not bad (also, FFS, what the compiler is doing if it's not able to optimize this?!). --- src/Magnum/Shaders/Phong.frag | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index 79792e3f9..fc5d30e71 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -419,6 +419,8 @@ void main() { normalizedTransformedNormal = tbn*(normalize((texture(normalTexture, interpolatedTextureCoordinates).rgb*2.0 - vec3(1.0))*vec3(normalTextureScale, normalTextureScale, 1.0))); #endif + highp const vec3 cameraDirection = normalize(-transformedPosition); + /* Add diffuse color for each light */ #ifndef UNIFORM_BUFFERS for(int i = 0; i < LIGHT_COUNT; ++i) @@ -475,7 +477,7 @@ void main() { if(intensity > 0.001) { highp vec3 reflection = reflect(-normalizedLightDirection, normalizedTransformedNormal); /* Use attenuation for the specularity as well */ - mediump float specularity = clamp(pow(max(0.0, dot(normalize(-transformedPosition), reflection)), shininess), 0.0, 1.0)*attenuation; + mediump float specularity = clamp(pow(max(0.0, dot(cameraDirection, reflection)), shininess), 0.0, 1.0)*attenuation; fragmentColor += vec4(finalSpecularColor.rgb*lightSpecularColor.rgb*specularity, finalSpecularColor.a); } } From 47b5940a89b3044f25de80c99c13a9d760ee5c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 9 Jun 2021 13:13:30 +0200 Subject: [PATCH 62/93] Shaders: microoptimize redundant square root. Just to have all options tried out, no significant impact this time again. --- src/Magnum/Shaders/Phong.frag | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index fc5d30e71..478fa2996 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -462,14 +462,15 @@ void main() { /* Attenuation. Directional lights have the .w component set to 0, use that to make the distance zero -- which will then ensure the attenuation is always 1.0 */ - highp float dist = length(lightDirection.xyz)*lightDirection.w; + highp const float len = length(lightDirection.xyz); + highp const float dist = len*lightDirection.w; /* If range is 0 for whatever reason, clamp it to a small value to avoid a NaN when dist is 0 as well (which is the case for directional lights). */ highp float attenuation = clamp(1.0 - pow(dist/max(lightRange, 0.0001), 4.0), 0.0, 1.0); attenuation = attenuation*attenuation/(1.0 + dist*dist); - highp vec3 normalizedLightDirection = normalize(lightDirection.xyz); + highp vec3 normalizedLightDirection = lightDirection.xyz/len; lowp float intensity = max(0.0, dot(normalizedTransformedNormal, normalizedLightDirection))*attenuation; fragmentColor.rgb += finalDiffuseColor.rgb*lightColor*intensity; From afd8d7c8f9754a66dd25c14bd4cc269855ea82f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 9 Jun 2021 15:32:07 +0200 Subject: [PATCH 63/93] Shaders: introduce Phong::Flag::LightCulling. After several failed attempts to make UBO performance not suck on Intel Mesa and Windows drivers, I ended up hiding the dynamic aspect under a flag. That way it's still possible to get the proper perf in UBO workflows that don't do light culling, and for workflows where light culling matters the 2x slowdown might be still better than looping through several extra lights that don't contribute anything. --- src/Magnum/Shaders/Phong.frag | 26 ++++- src/Magnum/Shaders/Phong.h | 30 +++--- src/Magnum/Shaders/PhongGL.cpp | 9 +- src/Magnum/Shaders/PhongGL.h | 28 ++++-- src/Magnum/Shaders/Test/PhongGLTest.cpp | 95 ++++++++++++++++++- .../Shaders/Test/ShadersGLBenchmark.cpp | 3 +- 6 files changed, 161 insertions(+), 30 deletions(-) diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index 478fa2996..fde89744a 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -422,7 +422,7 @@ void main() { highp const vec3 cameraDirection = normalize(-transformedPosition); /* Add diffuse color for each light */ - #ifndef UNIFORM_BUFFERS + #ifndef LIGHT_CULLING for(int i = 0; i < LIGHT_COUNT; ++i) #else for(uint i = 0u, actualLightCount = min(uint(LIGHT_COUNT), draws[drawId].draw_lightCount); i < actualLightCount; ++i) @@ -432,21 +432,33 @@ void main() { #ifndef UNIFORM_BUFFERS lightColors[i] #else - lights[lightOffset + i].light_color + lights[ + #ifdef LIGHT_CULLING + lightOffset + + #endif + i].light_color #endif ; lowp const vec3 lightSpecularColor = #ifndef UNIFORM_BUFFERS lightSpecularColors[i] #else - lights[lightOffset + i].light_specularColor + lights[ + #ifdef LIGHT_CULLING + lightOffset + + #endif + i].light_specularColor #endif ; lowp const float lightRange = #ifndef UNIFORM_BUFFERS lightRanges[i] #else - lights[lightOffset + i].light_range + lights[ + #ifdef LIGHT_CULLING + lightOffset + + #endif + i].light_range #endif ; @@ -454,7 +466,11 @@ void main() { #ifndef UNIFORM_BUFFERS lightPositions[i] #else - lights[lightOffset + i].position + lights[ + #ifdef LIGHT_CULLING + lightOffset + + #endif + i].position #endif ; highp const vec4 lightDirection = vec4(lightPosition.xyz - transformedPosition*lightPosition.w, lightPosition.w); diff --git a/src/Magnum/Shaders/Phong.h b/src/Magnum/Shaders/Phong.h index 8ac861067..2e469f30e 100644 --- a/src/Magnum/Shaders/Phong.h +++ b/src/Magnum/Shaders/Phong.h @@ -188,6 +188,9 @@ struct PhongDrawUniform { * References the first light in the @ref PhongLightUniform array. Should * be less than the light count passed to @ref PhongGL constructor. Default * value is @cpp 0 @ce. + * + * Used only if @ref PhongGL::Flag::LightCulling is enabled, otherwise + * light offset is implicitly @cpp 0 @ce. */ UnsignedInt lightOffset; @@ -198,6 +201,9 @@ struct PhongDrawUniform { * @ref PhongLightUniform array. Gets clamped by the shader so it's * together with @ref lightOffset not larger than the light count passed to * @ref PhongGL constructor. Default value is @cpp 0xffffffffu @ce. + * + * Used only if @ref PhongGL::Flag::LightCulling is enabled, otherwise + * light count is implicitly @ref PhongGL::lightCount(). */ UnsignedInt lightCount; }; @@ -307,9 +313,9 @@ struct PhongMaterialUniform { * * Default value is @cpp 0xffffffff_rgbaf @ce. * - * Used only if @ref PhongDrawUniform::lightCount is not zero, ignored - * otherwise. If @ref PhongGL::Flag::VertexColor is enabled, the color is - * multiplied with a color coming from the @ref PhongGL::Color3 / + * Used only if the effective light count for given draw is not zero, + * ignored otherwise. If @ref PhongGL::Flag::VertexColor is enabled, the + * color is multiplied with a color coming from the @ref PhongGL::Color3 / * @ref PhongGL::Color4 attribute. * @see @ref PhongGL::setDiffuseColor() */ @@ -320,8 +326,8 @@ struct PhongMaterialUniform { * * Default value is @cpp 0xffffff00_rgbaf @ce. * - * Used only if @ref PhongDrawUniform::lightCount is not zero, ignored - * otherwise. + * Used only if the effective light count for given draw is not zero, + * ignored otherwise. * @see @ref PhongGL::setSpecularColor() */ Color4 specularColor; @@ -333,8 +339,8 @@ struct PhongMaterialUniform { * meaning the normal texture is not changed in any way; a value of * @cpp 0.0f @ce disables the normal texture effect altogether. * - * Used only if @ref PhongGL::Flag::NormalTexture is enabled and - * @ref PhongDrawUniform::lightCount is not zero, ignored otherwise. + * Used only if @ref PhongGL::Flag::NormalTexture is enabled and the + * effective light count for given draw is not zero, ignored otherwise. * @see @ref PhongGL::setNormalTextureScale() */ Float normalTextureScale; @@ -345,8 +351,8 @@ struct PhongMaterialUniform { * The larger value, the harder surface (smaller specular highlight). * Default value is @cpp 80.0f @ce. * - * Used only if @ref PhongDrawUniform::lightCount is not zero, ignored - * otherwise. + * Used only if the effective light count for given draw is not zero, + * ignored otherwise. * @see @ref PhongGL::setShininess() */ Float shininess; @@ -378,8 +384,10 @@ struct PhongMaterialUniform { @brief Light parameters for Phong shaders @m_since_latest -Describes light properties referenced by the @ref PhongDrawUniform::lightOffset -and @ref PhongDrawUniform::lightCount range. +Describes light properties for each light used by the shader, either all +@ref PhongGL::lightCount() or the subrange referenced by the +@ref PhongDrawUniform::lightOffset and @ref PhongDrawUniform::lightCount range +if @ref PhongGL::Flag::LightCulling is enabled. @see @ref PhongGL::bindLightBuffer() */ struct PhongLightUniform { diff --git a/src/Magnum/Shaders/PhongGL.cpp b/src/Magnum/Shaders/PhongGL.cpp index 586f15cbe..12db5b999 100644 --- a/src/Magnum/Shaders/PhongGL.cpp +++ b/src/Magnum/Shaders/PhongGL.cpp @@ -107,6 +107,8 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount "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", ); + CORRADE_ASSERT(!(flags & Flag::LightCulling) || (flags & Flag::UniformBuffers), + "Shaders::PhongGL: light culling requires uniform buffers to be enabled", ); #endif #ifndef MAGNUM_TARGET_GLES @@ -252,7 +254,8 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount drawCount, materialCount, lightCount)); - frag.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); + frag.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : "") + .addSource(flags >= Flag::LightCulling ? "#define LIGHT_CULLING\n" : ""); } else #endif { @@ -915,6 +918,7 @@ Debug& operator<<(Debug& debug, const PhongGL::Flag value) { _c(UniformBuffers) _c(MultiDraw) _c(TextureArrays) + _c(LightCulling) #endif #undef _c /* LCOV_EXCL_STOP */ @@ -942,7 +946,8 @@ Debug& operator<<(Debug& debug, const PhongGL::Flags value) { #ifndef MAGNUM_TARGET_GLES2 PhongGL::Flag::MultiDraw, /* Superset of UniformBuffers */ PhongGL::Flag::UniformBuffers, - PhongGL::Flag::TextureArrays + PhongGL::Flag::TextureArrays, + PhongGL::Flag::LightCulling #endif }); } diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index aef2d67d2..a585e12ea 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -266,11 +266,12 @@ with one default light, would look like this: For a multidraw workflow enable @ref Flag::MultiDraw (and possibly @ref Flag::TextureArrays) and supply desired light, material and draw count in the @ref PhongGL(Flags, UnsignedInt, UnsignedInt, UnsignedInt) constructor. For -every draw then specify material references and texture offsets/layers, it's -also possible to perform per-draw light culling by supplying a subrange into -the @ref PhongLightUniform array using @ref PhongDrawUniform::lightOffset and -@relativeref{PhongDrawUniform,lightCount}. Besides that, the usage is similar -for all shaders, see @ref shaders-usage-multidraw for an example. +every draw then specify material references and texture offsets/layers. With +@ref Flag::LightCulling it's also possible to perform per-draw light culling by +supplying a subrange into the @ref PhongLightUniform array using +@ref PhongDrawUniform::lightOffset and @relativeref{PhongDrawUniform,lightCount}. +Besides that, the usage is similar for all shaders, see +@ref shaders-usage-multidraw for an example. @requires_gl30 Extension @gl_extension{EXT,texture_array} for texture arrays. @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} for uniform @@ -689,7 +690,22 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @requires_webgl20 Texture arrays are not available in WebGL 1.0. * @m_since_latest */ - TextureArrays = 1 << 14 + TextureArrays = 1 << 14, + + /** + * Enable light culling in uniform buffer workflows using the + * @ref PhongDrawUniform::lightOffset and + * @ref PhongDrawUniform::lightCount fields. If not enabled, all + * @ref lightCount() lights are used for every draw. Expects that + * @ref Flag::UniformBuffers is enabled as well. + * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES + * 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL + * 1.0. + * @m_since_latest + */ + LightCulling = 1 << 15 #endif }; diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 62208759d..01f144f0a 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -147,6 +147,9 @@ struct PhongGLTest: GL::OpenGLTester { void renderLightsSetOneByOne(); /* This tests just the algorithm, not affected by UBOs */ void renderLowLightAngle(); + #ifndef MAGNUM_TARGET_GLES2 + void renderLightCulling(); + #endif template void renderZeroLights(); @@ -253,6 +256,7 @@ constexpr struct { /* SwiftShader has 256 uniform vectors at most, per-3D-draw is 4+4, per-material 4, per-light 4 plus 4 for projection */ {"multiple lights, materials, draws", PhongGL::Flag::UniformBuffers, 8, 8, 24}, + {"multiple lights, materials, draws + light culling", PhongGL::Flag::UniformBuffers|PhongGL::Flag::LightCulling, 8, 8, 24}, {"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}, @@ -261,7 +265,7 @@ constexpr struct { {"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::TextureArrays|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|PhongGL::Flag::LightCulling, 8, 16, 24} }; #endif @@ -294,7 +298,9 @@ constexpr struct { {"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"} + "texture arrays require texture transformation enabled as well if uniform buffers are used"}, + {"light culling but no UBOs", PhongGL::Flag::LightCulling, 1, 1, 1, + "light culling requires uniform buffers to be enabled"} }; #endif @@ -899,8 +905,13 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); - addTests({&PhongGLTest::renderLightsSetOneByOne, - &PhongGLTest::renderLowLightAngle}, + addTests({ + &PhongGLTest::renderLightsSetOneByOne, + &PhongGLTest::renderLowLightAngle, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::renderLightCulling + #endif + }, &PhongGLTest::renderSetup, &PhongGLTest::renderTeardown); @@ -3044,6 +3055,80 @@ void PhongGLTest::renderLowLightAngle() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } +#ifndef MAGNUM_TARGET_GLES2 +void PhongGLTest::renderLightCulling() { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) + if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader) + CORRADE_SKIP("UBOs with dynamically indexed (light) arrays are a crashy dumpster fire on SwiftShader, can't test."); + #endif + + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); + + GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { + ProjectionUniform3D{} + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + }}; + GL::Buffer transformationUniform{GL::Buffer::TargetHint::Uniform, { + TransformationUniform3D{} + .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f))) + }}; + GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { + PhongDrawUniform{} + .setLightOffsetCount(57, 2) + }}; + GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { + PhongMaterialUniform{} + .setAmbientColor(0x330033_rgbf) + .setDiffuseColor(0xccffcc_rgbf) + .setSpecularColor(0x6666ff_rgbf) + }}; + /* Put one light into the first 32-bit component, one into the second to + test that both halves are checked correctly */ + PhongLightUniform lights[64]; + lights[57] = PhongLightUniform{} + .setPosition({-3.0f, -3.0f, 2.0f, 0.0f}) + .setColor(0x993366_rgbf); + lights[58] = PhongLightUniform{} + .setPosition({3.0f, -3.0f, 2.0f, 0.0f}) + .setColor(0x669933_rgbf); + GL::Buffer lightUniform{lights}; + + PhongGL shader{PhongGL::Flag::UniformBuffers|PhongGL::Flag::LightCulling, 64}; + shader + .bindProjectionBuffer(projectionUniform) + .bindTransformationBuffer(transformationUniform) + .bindDrawBuffer(drawUniform) + .bindMaterialBuffer(materialUniform) + .bindLightBuffer(lightUniform) + .draw(sphere); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found."); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has some minor rounding differences (max = 1). ARM Mali G71 + and Apple A8 has bigger rounding differences. */ + const Float maxThreshold = 8.34f, meanThreshold = 0.100f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 15.34f, meanThreshold = 3.33f; + #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, "PhongTestFiles/colored.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); +} +#endif + template void PhongGLTest::renderZeroLights() { if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) @@ -3613,7 +3698,7 @@ void PhongGLTest::renderMulti() { CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test."); #endif - PhongGL shader{PhongGL::Flag::UniformBuffers|PhongGL::Flag::ObjectId|data.flags, data.lightCount, data.materialCount, data.drawCount}; + PhongGL shader{PhongGL::Flag::UniformBuffers|PhongGL::Flag::ObjectId|PhongGL::Flag::LightCulling|data.flags, data.lightCount, data.materialCount, data.drawCount}; GL::Texture2D diffuse{NoCreate}; GL::Texture2DArray diffuseArray{NoCreate}; diff --git a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp index 5ec5d80f3..1569561e0 100644 --- a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp +++ b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp @@ -197,7 +197,8 @@ const struct { {"UBO single, ADS texture arrays + transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::TextureTransformation, 1, 1, 1}, {"UBO multi, one light", PhongGL::Flag::UniformBuffers, 1, 32, 128}, {"multidraw, one light", PhongGL::Flag::MultiDraw, 1, 32, 128}, - {"multidraw, 64 lights, five used", PhongGL::Flag::MultiDraw, 64, 32, 128}, + {"multidraw, one light, light culling enabled", PhongGL::Flag::MultiDraw|PhongGL::Flag::LightCulling, 1, 32, 128}, + {"multidraw, 64 lights, light culling enabled, five used", PhongGL::Flag::MultiDraw|PhongGL::Flag::LightCulling, 64, 32, 128}, #endif }; From a305a1ed85559ff892b60288d23e1a38569b03a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 9 Jun 2021 15:56:36 +0200 Subject: [PATCH 64/93] Shaders: benchmarks Phong UBOs with ARB_buffer_storage as well. Just to have at least one case where it's considered. Doesn't help that much, so probably not even worth bothering with. --- .../Shaders/Test/ShadersGLBenchmark.cpp | 106 ++++++++++++------ 1 file changed, 69 insertions(+), 37 deletions(-) diff --git a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp index 1569561e0..6a716eefd 100644 --- a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp +++ b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp @@ -166,39 +166,43 @@ const struct { const char* name; PhongGL::Flags flags; UnsignedInt lightCount, materialCount, drawCount; + bool bufferStorage; } PhongData[] { - {"", {}, 1, 1, 1}, - {"zero lights", {}, 0, 1, 1}, - {"five lights", {}, 5, 1, 1}, - {"vertex color", PhongGL::Flag::VertexColor, 1, 1, 1}, + {"", {}, 1, 1, 1, false}, + {"zero lights", {}, 0, 1, 1, false}, + {"five lights", {}, 5, 1, 1, false}, + {"vertex color", PhongGL::Flag::VertexColor, 1, 1, 1, false}, #ifndef MAGNUM_TARGET_GLES2 - {"object ID", PhongGL::Flag::ObjectId, 1, 1, 1}, + {"object ID", PhongGL::Flag::ObjectId, 1, 1, 1, false}, #endif - {"diffuse texture", PhongGL::Flag::DiffuseTexture, 1, 1, 1}, - {"ADS textures", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture, 1, 1, 1}, + {"diffuse texture", PhongGL::Flag::DiffuseTexture, 1, 1, 1, false}, + {"ADS textures", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture, 1, 1, 1, false}, #ifndef MAGNUM_TARGET_GLES2 - {"ADS texture arrays", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureArrays, 1, 1, 1}, - #endif - {"ADS textures + alpha mask", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::AlphaMask, 1, 1, 1}, - {"ADS textures + transformation", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureTransformation, 1, 1, 1}, - {"normal texture", PhongGL::Flag::NormalTexture, 1, 1, 1}, - {"normal texture with separate bitangent", PhongGL::Flag::NormalTexture|PhongGL::Flag::Bitangent, 1, 1, 1}, - {"instanced transformation", PhongGL::Flag::InstancedTransformation, 1, 1, 1}, - {"instanced transformation + color", PhongGL::Flag::InstancedTransformation|PhongGL::Flag::VertexColor, 1, 1, 1}, + {"ADS texture arrays", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureArrays, 1, 1, 1, false}, + #endif + {"ADS textures + alpha mask", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::AlphaMask, 1, 1, 1, false}, + {"ADS textures + transformation", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureTransformation, 1, 1, 1, false}, + {"normal texture", PhongGL::Flag::NormalTexture, 1, 1, 1, false}, + {"normal texture with separate bitangent", PhongGL::Flag::NormalTexture|PhongGL::Flag::Bitangent, 1, 1, 1, false}, + {"instanced transformation", PhongGL::Flag::InstancedTransformation, 1, 1, 1, false}, + {"instanced transformation + color", PhongGL::Flag::InstancedTransformation|PhongGL::Flag::VertexColor, 1, 1, 1, false}, #ifndef MAGNUM_TARGET_GLES2 - {"instanced transformation + object ID", PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedObjectId, 1, 1, 1}, + {"instanced transformation + object ID", PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedObjectId, 1, 1, 1, false}, #endif - {"instanced transformation + ADS texture offset", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedTextureOffset, 1, 1, 1}, + {"instanced transformation + ADS texture offset", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedTextureOffset, 1, 1, 1, false}, #ifndef MAGNUM_TARGET_GLES2 - {"UBO single", PhongGL::Flag::UniformBuffers, 1, 1, 1}, - {"UBO single, zero lights", PhongGL::Flag::UniformBuffers, 0, 1, 1}, - {"UBO single five lights", PhongGL::Flag::UniformBuffers, 5, 1, 1}, - {"UBO single, ADS textures + transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureTransformation, 1, 1, 1}, - {"UBO single, ADS texture arrays + transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::TextureTransformation, 1, 1, 1}, - {"UBO multi, one light", PhongGL::Flag::UniformBuffers, 1, 32, 128}, - {"multidraw, one light", PhongGL::Flag::MultiDraw, 1, 32, 128}, - {"multidraw, one light, light culling enabled", PhongGL::Flag::MultiDraw|PhongGL::Flag::LightCulling, 1, 32, 128}, - {"multidraw, 64 lights, light culling enabled, five used", PhongGL::Flag::MultiDraw|PhongGL::Flag::LightCulling, 64, 32, 128}, + {"UBO single", PhongGL::Flag::UniformBuffers, 1, 1, 1, false}, + {"UBO single, zero lights", PhongGL::Flag::UniformBuffers, 0, 1, 1, false}, + {"UBO single five lights", PhongGL::Flag::UniformBuffers, 5, 1, 1, false}, + {"UBO single, ADS textures + transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureTransformation, 1, 1, 1, false}, + {"UBO single, ADS texture arrays + transformation", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::TextureTransformation, 1, 1, 1, false}, + {"UBO multi, one light", PhongGL::Flag::UniformBuffers, 1, 32, 128, false}, + {"multidraw, one light", PhongGL::Flag::MultiDraw, 1, 32, 128, false}, + #ifndef MAGNUM_TARGET_GLES + {"multidraw, one light, immutable buffer storage", PhongGL::Flag::MultiDraw, 1, 32, 128, true}, + #endif + {"multidraw, one light, light culling enabled", PhongGL::Flag::MultiDraw|PhongGL::Flag::LightCulling, 1, 32, 128, false}, + {"multidraw, 64 lights, light culling enabled, five used", PhongGL::Flag::MultiDraw|PhongGL::Flag::LightCulling, 64, 32, 128, false}, #endif }; @@ -692,30 +696,58 @@ void ShadersGLBenchmark::phong() { GL::Buffer lightUniform{NoCreate}; GL::Buffer textureTransformationUniform{NoCreate}; if(data.flags & PhongGL::Flag::UniformBuffers) { - projectionUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, { - ProjectionUniform3D{} - }}; - transformationUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; + projectionUniform = GL::Buffer{}; + transformationUniform = GL::Buffer{}; + drawUniform = GL::Buffer{}; + materialUniform = GL::Buffer{}; + lightUniform = GL::Buffer{}; + textureTransformationUniform = GL::Buffer{}; + + Containers::Array transformationData{data.drawCount}; Containers::Array drawData{data.drawCount}; drawData[0].lightCount = 5; /* Cap at 5 lights, even if more is set */ - drawUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, drawData}; - Containers::Array materialData{data.materialCount}; materialData[0] /* White ambient so we always have a white output */ .setAmbientColor(0xffffffff_rgbaf) .setAlphaMask(0.0f); - materialUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, materialData}; - lightUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.lightCount}}; + Containers::Array lightData{data.lightCount}; + Containers::Array textureTransformationData{data.drawCount}; + + #ifndef MAGNUM_TARGET_GLES + if(data.bufferStorage) { + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::buffer_storage::string() << "is not supported."); + + projectionUniform.setStorage(Containers::arrayView({ProjectionUniform3D{}}), {}); + transformationUniform.setStorage(transformationData, {}); + drawUniform.setStorage(drawData, {}); + materialUniform.setStorage(materialData, {}); + lightUniform.setStorage(lightData, {}); + + if(data.flags & PhongGL::Flag::TextureTransformation) + textureTransformationUniform.setStorage(textureTransformationData, {}); + } else + #endif + { + projectionUniform.setData({ProjectionUniform3D{}}); + transformationUniform.setData(transformationData); + drawUniform.setData(drawData); + materialUniform.setData(materialData); + lightUniform.setData(lightData); + + if(data.flags & PhongGL::Flag::TextureTransformation) + textureTransformationUniform.setData(textureTransformationData); + } + shader.bindProjectionBuffer(projectionUniform) .bindTransformationBuffer(transformationUniform) .bindDrawBuffer(drawUniform) .bindMaterialBuffer(materialUniform) .bindLightBuffer(lightUniform); - if(data.flags & PhongGL::Flag::TextureTransformation) { - textureTransformationUniform = GL::Buffer{GL::Buffer::TargetHint::Uniform, Containers::Array{data.drawCount}}; + if(data.flags & PhongGL::Flag::TextureTransformation) shader.bindTextureTransformationBuffer(textureTransformationUniform); - } + } else #endif { From cc74784d40052f610eb4128c693c3247fcb45990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 9 Jun 2021 17:09:54 +0200 Subject: [PATCH 65/93] Shaders: ability to disable Phong specular contribution. In cases when specular highlights are not desired, results in 30% speedup (on Intel) and ~25% speedup on AMD, compared to setting the specular color to transparent black. Testing was easy thanks to already having a ground truth image for this case. --- doc/changelog.dox | 3 + src/Magnum/Shaders/Phong.frag | 10 +++ src/Magnum/Shaders/Phong.h | 8 +-- src/Magnum/Shaders/PhongGL.cpp | 36 ++++++++--- src/Magnum/Shaders/PhongGL.h | 32 +++++++--- src/Magnum/Shaders/Test/CMakeLists.txt | 2 +- src/Magnum/Shaders/Test/PhongGLTest.cpp | 57 +++++++++++++++--- src/Magnum/Shaders/Test/PhongGL_Test.cpp | 4 +- ...specular.tga => shininess-no-specular.tga} | Bin .../Shaders/Test/ShadersGLBenchmark.cpp | 1 + 10 files changed, 119 insertions(+), 34 deletions(-) rename src/Magnum/Shaders/Test/PhongTestFiles/{shininess-black-specular.tga => shininess-no-specular.tga} (100%) diff --git a/doc/changelog.dox b/doc/changelog.dox index a805c3a9a..10e6d9f30 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -161,6 +161,9 @@ See also: @ref Trade::LightData - Added @ref Shaders::PhongGL::setLightSpecularColors() for better control over specular highlights +- Added @ref Shaders::PhongGL::Flag::NoSpecular as a significantly faster + alternative to setting specular color to @cpp 0x00000000_rgbaf @ce in case + specular highlights are not desired @subsubsection changelog-latest-new-shadertools ShaderTools library diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index fde89744a..1c21e5b0c 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -63,6 +63,7 @@ uniform lowp vec4 diffuseColor #endif ; +#ifndef NO_SPECULAR #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 7) #endif @@ -81,6 +82,7 @@ uniform mediump float shininess #endif ; #endif +#endif #ifdef NORMAL_TEXTURE #ifdef EXPLICIT_UNIFORM_LOCATION @@ -136,6 +138,7 @@ uniform lowp vec3 lightColors[LIGHT_COUNT] #endif ; +#ifndef NO_SPECULAR #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = LIGHT_SPECULAR_COLORS_LOCATION) #endif @@ -144,6 +147,7 @@ uniform lowp vec3 lightSpecularColors[LIGHT_COUNT] = vec3[](LIGHT_COLOR_INITIALIZER) #endif ; +#endif #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = LIGHT_RANGES_LOCATION) @@ -386,12 +390,14 @@ void main() { interpolatedVertexColor* #endif diffuseColor; + #ifndef NO_SPECULAR lowp const vec4 finalSpecularColor = #ifdef SPECULAR_TEXTURE texture(specularTexture, interpolatedTextureCoordinates)* #endif specularColor; #endif + #endif /* Ambient color */ fragmentColor = finalAmbientColor; @@ -439,6 +445,7 @@ void main() { i].light_color #endif ; + #ifndef NO_SPECULAR lowp const vec3 lightSpecularColor = #ifndef UNIFORM_BUFFERS lightSpecularColors[i] @@ -450,6 +457,7 @@ void main() { i].light_specularColor #endif ; + #endif lowp const float lightRange = #ifndef UNIFORM_BUFFERS lightRanges[i] @@ -490,6 +498,7 @@ void main() { lowp float intensity = max(0.0, dot(normalizedTransformedNormal, normalizedLightDirection))*attenuation; fragmentColor.rgb += finalDiffuseColor.rgb*lightColor*intensity; + #ifndef NO_SPECULAR /* Add specular color, if needed */ if(intensity > 0.001) { highp vec3 reflection = reflect(-normalizedLightDirection, normalizedTransformedNormal); @@ -497,6 +506,7 @@ void main() { mediump float specularity = clamp(pow(max(0.0, dot(cameraDirection, reflection)), shininess), 0.0, 1.0)*attenuation; fragmentColor += vec4(finalSpecularColor.rgb*lightSpecularColor.rgb*specularity, finalSpecularColor.a); } + #endif } fragmentColor.a += finalDiffuseColor.a; diff --git a/src/Magnum/Shaders/Phong.h b/src/Magnum/Shaders/Phong.h index 2e469f30e..b9b76ce93 100644 --- a/src/Magnum/Shaders/Phong.h +++ b/src/Magnum/Shaders/Phong.h @@ -326,8 +326,8 @@ struct PhongMaterialUniform { * * Default value is @cpp 0xffffff00_rgbaf @ce. * - * Used only if the effective light count for given draw is not zero, - * ignored otherwise. + * Used only if the effective light count for given draw is not zero and + * @ref PhongGL::Flag::NoSpecular is not set, ignored otherwise. * @see @ref PhongGL::setSpecularColor() */ Color4 specularColor; @@ -351,8 +351,8 @@ struct PhongMaterialUniform { * The larger value, the harder surface (smaller specular highlight). * Default value is @cpp 80.0f @ce. * - * Used only if the effective light count for given draw is not zero, - * ignored otherwise. + * Used only if the effective light count for given draw is not zero and + * @ref PhongGL::Flag::NoSpecular is not set, ignored otherwise. * @see @ref PhongGL::setShininess() */ Float shininess; diff --git a/src/Magnum/Shaders/PhongGL.cpp b/src/Magnum/Shaders/PhongGL.cpp index 12db5b999..57723011f 100644 --- a/src/Magnum/Shaders/PhongGL.cpp +++ b/src/Magnum/Shaders/PhongGL.cpp @@ -111,6 +111,9 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount "Shaders::PhongGL: light culling requires uniform buffers to be enabled", ); #endif + CORRADE_ASSERT(!(flags & Flag::SpecularTexture) || !(flags & (Flag::NoSpecular)), + "Shaders::PhongGL: specular texture requires the shader to not have specular disabled", ); + #ifndef MAGNUM_TARGET_GLES if(flags >= Flag::UniformBuffers) MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); @@ -243,6 +246,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount .addSource(flags & Flag::ObjectId ? "#define OBJECT_ID\n" : "") .addSource(flags >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") #endif + .addSource(flags & Flag::NoSpecular ? "#define NO_SPECULAR\n" : "") ; #ifndef MAGNUM_TARGET_GLES2 if(flags >= Flag::UniformBuffers) { @@ -338,13 +342,16 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount if(lightCount) { _normalMatrixUniform = uniformLocation("normalMatrix"); _diffuseColorUniform = uniformLocation("diffuseColor"); - _specularColorUniform = uniformLocation("specularColor"); - _shininessUniform = uniformLocation("shininess"); + if(!(flags & Flag::NoSpecular)) { + _specularColorUniform = uniformLocation("specularColor"); + _shininessUniform = uniformLocation("shininess"); + } if(flags & Flag::NormalTexture) _normalTextureScaleUniform = uniformLocation("normalTextureScale"); _lightPositionsUniform = uniformLocation("lightPositions"); _lightColorsUniform = uniformLocation("lightColors"); - _lightSpecularColorsUniform = uniformLocation("lightSpecularColors"); + if(!(flags & Flag::NoSpecular)) + _lightSpecularColorsUniform = uniformLocation("lightSpecularColors"); _lightRangesUniform = uniformLocation("lightRanges"); } if(flags & Flag::AlphaMask) _alphaMaskUniform = uniformLocation("alphaMask"); @@ -393,14 +400,17 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount setProjectionMatrix(Matrix4{Math::IdentityInit}); if(lightCount) { setDiffuseColor(Magnum::Color4{1.0f}); - setSpecularColor(Magnum::Color4{1.0f, 0.0f}); - setShininess(80.0f); + if(!(flags & Flag::NoSpecular)) { + setSpecularColor(Magnum::Color4{1.0f, 0.0f}); + setShininess(80.0f); + } if(flags & Flag::NormalTexture) setNormalTextureScale(1.0f); setLightPositions(Containers::Array{DirectInit, lightCount, Vector4{0.0f, 0.0f, 1.0f, 0.0f}}); Containers::Array colors{DirectInit, lightCount, Magnum::Color3{1.0f}}; setLightColors(colors); - setLightSpecularColors(colors); + if(!(flags & Flag::NoSpecular)) + setLightSpecularColors(colors); setLightRanges(Containers::Array{DirectInit, lightCount, Constants::inf()}); /* Light position is zero by default */ setNormalMatrix(Matrix3x3{Math::IdentityInit}); @@ -441,6 +451,8 @@ PhongGL& PhongGL::setSpecularColor(const Magnum::Color4& color) { CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), "Shaders::PhongGL::setSpecularColor(): the shader was created with uniform buffers enabled", *this); #endif + CORRADE_ASSERT(!(_flags >= Flag::NoSpecular), + "Shaders::PhongGL::setSpecularColor(): the shader was created with specular disabled", *this); if(_lightCount) setUniform(_specularColorUniform, color); return *this; } @@ -450,6 +462,8 @@ PhongGL& PhongGL::setShininess(Float shininess) { CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), "Shaders::PhongGL::setShininess(): the shader was created with uniform buffers enabled", *this); #endif + CORRADE_ASSERT(!(_flags >= Flag::NoSpecular), + "Shaders::PhongGL::setShininess(): the shader was created with specular disabled", *this); if(_lightCount) setUniform(_shininessUniform, shininess); return *this; } @@ -651,6 +665,8 @@ PhongGL& PhongGL::setLightSpecularColors(const Containers::ArrayView= Flag::NoSpecular), + "Shaders::PhongGL::setLightSpecularColors(): the shader was created with specular disabled", *this); if(_lightCount) setUniform(_lightSpecularColorsUniform, colors); return *this; } @@ -666,6 +682,8 @@ PhongGL& PhongGL::setLightSpecularColor(const UnsignedInt id, const Magnum::Colo #endif CORRADE_ASSERT(id < _lightCount, "Shaders::PhongGL::setLightSpecularColor(): light ID" << id << "is out of bounds for" << _lightCount << "lights", *this); + CORRADE_ASSERT(!(_flags >= Flag::NoSpecular), + "Shaders::PhongGL::setLightSpecularColor(): the shader was created with specular disabled", *this); setUniform(_lightSpecularColorsUniform + id, color); return *this; } @@ -920,11 +938,12 @@ Debug& operator<<(Debug& debug, const PhongGL::Flag value) { _c(TextureArrays) _c(LightCulling) #endif + _c(NoSpecular) #undef _c /* LCOV_EXCL_STOP */ } - return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedInt(value)) << Debug::nospace << ")"; } Debug& operator<<(Debug& debug, const PhongGL::Flags value) { @@ -947,8 +966,9 @@ Debug& operator<<(Debug& debug, const PhongGL::Flags value) { PhongGL::Flag::MultiDraw, /* Superset of UniformBuffers */ PhongGL::Flag::UniformBuffers, PhongGL::Flag::TextureArrays, - PhongGL::Flag::LightCulling + PhongGL::Flag::LightCulling, #endif + PhongGL::Flag::NoSpecular }); } diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index a585e12ea..5c1f014fe 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -484,7 +484,7 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * * @see @ref Flags, @ref flags() */ - enum class Flag: UnsignedShort { + enum class Flag: UnsignedInt { /** * Multiply ambient color with a texture. * @see @ref setAmbientColor(), @ref bindAmbientTexture() @@ -705,8 +705,17 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * 1.0. * @m_since_latest */ - LightCulling = 1 << 15 + LightCulling = 1 << 15, #endif + + /** + * Disable specular contribution in light calculation. Can result + * in a significant performance improvement compared to calling + * @ref setSpecularColor() with @cpp 0x00000000_rgbaf @ce when + * specular highlights are not desired. + * @m_since_latest + */ + NoSpecular = 1 << 16 }; /** @@ -900,10 +909,13 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @brief Set specular color * @return Reference to self (for method chaining) * - * Initial value is @cpp 0xffffff00_rgbaf @ce. If + * Initial value is @cpp 0xffffff00_rgbaf @ce. Expects that the shader + * was not created with @ref Flag::NoSpecular. If * @ref Flag::SpecularTexture is set, the color will be multiplied with - * the texture. If you want to have a fully diffuse material, set - * the specular color to @cpp 0x00000000_rgbaf @ce. If + * the texture. If you want to have a fully diffuse material, it's + * recommended to disable the specular contribution altogether with + * @ref Flag::NoSpecular. If having a dedicated shader variant is not + * possible, set the specular color to @cpp 0x00000000_rgbaf @ce. If * @ref lightCount() is zero, this function is a no-op, as specular * color doesn't contribute to the output in that case. * @@ -1530,7 +1542,8 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @return Reference to self (for method chaining) * * Expects that the shader was created with @ref Flag::SpecularTexture - * enabled. If @ref Flag::TextureArrays is enabled as well, use + * enabled and that @ref Flag::NoSpecular is not set. 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. @@ -1545,9 +1558,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @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::SpecularTexture and @ref Flag::TextureArrays enabled and + * that @ref Flag::NoSpecular is not set. 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 diff --git a/src/Magnum/Shaders/Test/CMakeLists.txt b/src/Magnum/Shaders/Test/CMakeLists.txt index 277a3da9f..021acb252 100644 --- a/src/Magnum/Shaders/Test/CMakeLists.txt +++ b/src/Magnum/Shaders/Test/CMakeLists.txt @@ -280,7 +280,7 @@ if(BUILD_GL_TESTS) PhongTestFiles/instanced.tga PhongTestFiles/instanced-normal.tga PhongTestFiles/low-light-angle.tga - PhongTestFiles/shininess-black-specular.tga + PhongTestFiles/shininess-no-specular.tga PhongTestFiles/shininess0-overflow.tga PhongTestFiles/shininess0.tga PhongTestFiles/shininess10.tga diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 01f144f0a..9ab585b29 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -100,6 +100,7 @@ struct PhongGLTest: GL::OpenGLTester { void bindTextureArraysInvalid(); #endif void setAlphaMaskNotEnabled(); + void setSpecularDisabled(); void setTextureMatrixNotEnabled(); void setNormalTextureScaleNotEnabled(); #ifndef MAGNUM_TARGET_GLES2 @@ -233,6 +234,7 @@ constexpr struct { {"instanced object ID", PhongGL::Flag::InstancedObjectId, 1}, {"object ID + alpha mask + specular texture", PhongGL::Flag::ObjectId|PhongGL::Flag::AlphaMask|PhongGL::Flag::SpecularTexture, 1}, #endif + {"no specular", PhongGL::Flag::NoSpecular, 1}, {"five lights", {}, 5}, {"zero lights", {}, 0}, {"instanced transformation", PhongGL::Flag::InstancedTransformation, 3}, @@ -265,6 +267,7 @@ constexpr struct { {"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}, + {"no specular", PhongGL::Flag::UniformBuffers|PhongGL::Flag::NoSpecular, 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::TextureArrays|PhongGL::Flag::AlphaMask|PhongGL::Flag::ObjectId|PhongGL::Flag::InstancedTextureOffset|PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedObjectId|PhongGL::Flag::LightCulling, 8, 16, 24} }; #endif @@ -284,6 +287,9 @@ constexpr struct { PhongGL::Flag::Bitangent|PhongGL::Flag::InstancedObjectId, "Bitangent attribute binding conflicts with the ObjectId attribute, use a Tangent4 attribute with instanced object ID rendering instead"}, #endif + {"specular texture but no specular", + PhongGL::Flag::SpecularTexture|PhongGL::Flag::NoSpecular, + "specular texture requires the shader to not have specular disabled"} }; #ifndef MAGNUM_TARGET_GLES2 @@ -472,14 +478,22 @@ const struct { const struct { const char* name; const char* expected; + PhongGL::Flags flags; Float shininess; Color4 specular; } RenderShininessData[] { - {"80", "shininess80.tga", 80.0f, 0xffffff_rgbf}, - {"10", "shininess10.tga", 10.0f, 0xffffff_rgbf}, - {"0", "shininess0.tga", 0.0f, 0xffffff_rgbf}, - {"0.001", "shininess0.tga", 0.001f, 0xffffff_rgbf}, - {"black specular", "shininess-black-specular.tga", 80.0f, 0x000000_rgbf} + {"80", "shininess80.tga", + {}, 80.0f, 0xffffff_rgbf}, + {"10", "shininess10.tga", + {}, 10.0f, 0xffffff_rgbf}, + {"0", "shininess0.tga", + {}, 0.0f, 0xffffff_rgbf}, + {"0.001", "shininess0.tga", + {}, 0.001f, 0xffffff_rgbf}, + {"black specular", "shininess-no-specular.tga", + {}, 80.0f, 0x000000_rgbf}, + {"no specular", "shininess-no-specular.tga", + PhongGL::Flag::NoSpecular, 80.0f, 0xffffff_rgbf} }; const struct { @@ -777,6 +791,7 @@ PhongGLTest::PhongGLTest() { addTests({ &PhongGLTest::setAlphaMaskNotEnabled, + &PhongGLTest::setSpecularDisabled, &PhongGLTest::setTextureMatrixNotEnabled, &PhongGLTest::setNormalTextureScaleNotEnabled, #ifndef MAGNUM_TARGET_GLES2 @@ -1306,6 +1321,27 @@ void PhongGLTest::setAlphaMaskNotEnabled() { "Shaders::PhongGL::setAlphaMask(): the shader was not created with alpha mask enabled\n"); } +void PhongGLTest::setSpecularDisabled() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + + GL::Texture2D texture; + PhongGL shader{PhongGL::Flag::NoSpecular}; + shader.setSpecularColor({}) + .setShininess({}) + .setLightSpecularColors({{}}) + .setLightSpecularColor(0, {}); + CORRADE_COMPARE(out.str(), + "Shaders::PhongGL::setSpecularColor(): the shader was created with specular disabled\n" + "Shaders::PhongGL::setShininess(): the shader was created with specular disabled\n" + "Shaders::PhongGL::setLightSpecularColors(): the shader was created with specular disabled\n" + "Shaders::PhongGL::setLightSpecularColor(): the shader was created with specular disabled\n"); +} + void PhongGLTest::setTextureMatrixNotEnabled() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -2427,13 +2463,14 @@ template void PhongGLTest::renderShininess() { GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); - PhongGL shader{flag}; + PhongGL shader{flag|data.flags}; if(flag == PhongGL::Flag{}) { + if(!(data.flags & PhongGL::Flag::NoSpecular)) shader + .setSpecularColor(data.specular) + .setShininess(data.shininess); shader .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}}) .setDiffuseColor(0xff3333_rgbf) - .setSpecularColor(data.specular) - .setShininess(data.shininess) .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f))) .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) .draw(sphere); @@ -2459,8 +2496,8 @@ template void PhongGLTest::renderShininess() { GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { PhongMaterialUniform{} .setDiffuseColor(0xff3333_rgbf) - .setSpecularColor(data.specular) - .setShininess(data.shininess) + .setSpecularColor(data.specular) /* ignored if NoSpecular */ + .setShininess(data.shininess) /* ignored if NoSpecular */ }}; shader .bindProjectionBuffer(projectionUniform) diff --git a/src/Magnum/Shaders/Test/PhongGL_Test.cpp b/src/Magnum/Shaders/Test/PhongGL_Test.cpp index b00618d21..07f1f9ea9 100644 --- a/src/Magnum/Shaders/Test/PhongGL_Test.cpp +++ b/src/Magnum/Shaders/Test/PhongGL_Test.cpp @@ -72,8 +72,8 @@ void PhongGL_Test::constructCopy() { void PhongGL_Test::debugFlag() { std::ostringstream out; - Debug{&out} << PhongGL::Flag::AmbientTexture << PhongGL::Flag(0xf0); - CORRADE_COMPARE(out.str(), "Shaders::PhongGL::Flag::AmbientTexture Shaders::PhongGL::Flag(0xf0)\n"); + Debug{&out} << PhongGL::Flag::AmbientTexture << PhongGL::Flag(0xcafedead); + CORRADE_COMPARE(out.str(), "Shaders::PhongGL::Flag::AmbientTexture Shaders::PhongGL::Flag(0xcafedead)\n"); } void PhongGL_Test::debugFlags() { diff --git a/src/Magnum/Shaders/Test/PhongTestFiles/shininess-black-specular.tga b/src/Magnum/Shaders/Test/PhongTestFiles/shininess-no-specular.tga similarity index 100% rename from src/Magnum/Shaders/Test/PhongTestFiles/shininess-black-specular.tga rename to src/Magnum/Shaders/Test/PhongTestFiles/shininess-no-specular.tga diff --git a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp index 6a716eefd..da0791c81 100644 --- a/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp +++ b/src/Magnum/Shaders/Test/ShadersGLBenchmark.cpp @@ -171,6 +171,7 @@ const struct { {"", {}, 1, 1, 1, false}, {"zero lights", {}, 0, 1, 1, false}, {"five lights", {}, 5, 1, 1, false}, + {"no specular", PhongGL::Flag::NoSpecular, 1, 1, 1, false}, {"vertex color", PhongGL::Flag::VertexColor, 1, 1, 1, false}, #ifndef MAGNUM_TARGET_GLES2 {"object ID", PhongGL::Flag::ObjectId, 1, 1, 1, false}, From 37fc638d12ded5c8c6f30dc6b7ffe759b1ba57d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 9 Jun 2021 17:24:04 +0200 Subject: [PATCH 66/93] package/ci: had to upgrade to 10.15 because of MoltenVK. Sigh, that's like those projects that require GCC 13 to build. --- package/ci/circleci.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/package/ci/circleci.yml b/package/ci/circleci.yml index a07acb662..67c40fbb2 100644 --- a/package/ci/circleci.yml +++ b/package/ci/circleci.yml @@ -16,9 +16,9 @@ executors: ubuntu-18_04: docker: - image: ubuntu:bionic-20200921 - xcode-10_3: + xcode-11_2: macos: - xcode: 10.3.0 + xcode: 11.2.1 xcode-11_6: macos: xcode: 11.6.0 @@ -392,8 +392,9 @@ jobs: macos-gl: # Molten-vk isn't in (non-updated) Homebrew on the 9.4 or 10.0/1/2 image, - # have to use 10.3 instead - executor: xcode-10_3 + # have to use 10.3 instead; since 2021-06-08 it refuses to work on 10.14 so + # have to use 11.2 at least + executor: xcode-11_2 environment: CMAKE_CXX_FLAGS: --coverage CONFIGURATION: Debug @@ -425,8 +426,9 @@ jobs: macos-static: # Molten-vk isn't in (non-updated) Homebrew on the 9.4 or 10.0/1/2 image, - # have to use 10.3 instead - executor: xcode-10_3 + # have to use 10.3 instead; since 2021-06-08 it refuses to work on 10.14 so + # have to use 11.2 at least + executor: xcode-11_2 environment: # STUPID yml interprets unquoted ON as a boolean BUILD_STATIC: "ON" From 1cfa2d0fce7f791b8845b671d1b1b168be32c774 Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Tue, 15 Jun 2021 09:10:46 +0200 Subject: [PATCH 67/93] GL: remove an unneeded std::move(). --- src/Magnum/GL/Context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magnum/GL/Context.cpp b/src/Magnum/GL/Context.cpp index 53d9463bf..2fbc7d6b2 100644 --- a/src/Magnum/GL/Context.cpp +++ b/src/Magnum/GL/Context.cpp @@ -751,7 +751,7 @@ Context::Context(Context&& other) noexcept: #ifdef MAGNUM_BUILD_DEPRECATED _supportedExtensions{std::move(other._supportedExtensions)}, #endif - _state{std::move(other._state)}, + _state{other._state}, _detectedDrivers{std::move(other._detectedDrivers)}, _driverWorkarounds{std::move(other._driverWorkarounds)}, _disabledExtensions{std::move(other._disabledExtensions)}, From ef77fac6594861e0305a7c141ed775f53b69cc61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 15 Jun 2021 09:20:13 +0200 Subject: [PATCH 68/93] Animation: explicitly mark Track move construction/assignment as noexcept. Interestingly enough, GCC reported these were noexcept even without that annotation. Co-authored-by: Aaron Gokaslan --- src/Magnum/Animation/Test/TrackTest.cpp | 20 ++++++++++++++++++++ src/Magnum/Animation/Track.h | 4 ++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Magnum/Animation/Test/TrackTest.cpp b/src/Magnum/Animation/Test/TrackTest.cpp index 5b1bc79d6..a77ed079a 100644 --- a/src/Magnum/Animation/Test/TrackTest.cpp +++ b/src/Magnum/Animation/Test/TrackTest.cpp @@ -48,6 +48,9 @@ struct TrackTest: TestSuite::Tester { void constructInitializerListInterpolationInterpolator(); void constructInitializerListInterpolationInterpolatorDefaults(); + void constructCopy(); + void constructMove(); + void convertView(); void at(); @@ -106,6 +109,9 @@ TrackTest::TrackTest() { &TrackTest::constructInitializerListInterpolationInterpolator, &TrackTest::constructInitializerListInterpolationInterpolatorDefaults, + &TrackTest::constructCopy, + &TrackTest::constructMove, + &TrackTest::convertView}); addInstancedTests({&TrackTest::at, @@ -389,6 +395,20 @@ void TrackTest::constructInitializerListInterpolationInterpolatorDefaults() { CORRADE_COMPARE(a.values()[0], (Vector3{3.0f, 1.0f, 0.1f})); } +void TrackTest::constructCopy() { + CORRADE_VERIFY(!std::is_copy_constructible>::value); + CORRADE_VERIFY(!std::is_copy_assignable>::value); +} + +void TrackTest::constructMove() { + /* The move is defaulted, so verify just the right attributes */ + + CORRADE_VERIFY(std::is_nothrow_move_constructible>::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable>::value); + CORRADE_VERIFY(std::is_nothrow_move_constructible>::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable>::value); +} + void TrackTest::convertView() { Track a{ {{1.0f, {3.0f, 1.0f, 0.1f}}, diff --git a/src/Magnum/Animation/Track.h b/src/Magnum/Animation/Track.h index 25b1ed463..3b2d9d146 100644 --- a/src/Magnum/Animation/Track.h +++ b/src/Magnum/Animation/Track.h @@ -234,13 +234,13 @@ template&) = delete; /** @brief Move constructor */ - Track(Track&&) = default; + Track(Track&&) noexcept = default; /** @brief Copying is not allowed */ Track& operator=(const Track&) = delete; /** @brief Move constructor */ - Track& operator=(Track&&) = default; + Track& operator=(Track&&) noexcept = default; /** @brief Conversion to a view */ operator TrackView() const noexcept { From 712a3b635f41ba05be776d953060e1d84c6b3633 Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Tue, 15 Jun 2021 09:24:27 +0200 Subject: [PATCH 69/93] Platform: mark Windowless*Context moves as noexcept. --- src/Magnum/Platform/WindowlessCglApplication.cpp | 4 ++-- src/Magnum/Platform/WindowlessCglApplication.h | 4 ++-- src/Magnum/Platform/WindowlessEglApplication.cpp | 4 ++-- src/Magnum/Platform/WindowlessEglApplication.h | 4 ++-- src/Magnum/Platform/WindowlessGlxApplication.cpp | 4 ++-- src/Magnum/Platform/WindowlessGlxApplication.h | 4 ++-- src/Magnum/Platform/WindowlessIosApplication.h | 4 ++-- src/Magnum/Platform/WindowlessIosApplication.mm | 4 ++-- src/Magnum/Platform/WindowlessWglApplication.cpp | 4 ++-- src/Magnum/Platform/WindowlessWglApplication.h | 4 ++-- src/Magnum/Platform/WindowlessWindowsEglApplication.cpp | 4 ++-- src/Magnum/Platform/WindowlessWindowsEglApplication.h | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Magnum/Platform/WindowlessCglApplication.cpp b/src/Magnum/Platform/WindowlessCglApplication.cpp index ed8efa5d4..c92f9cfc4 100644 --- a/src/Magnum/Platform/WindowlessCglApplication.cpp +++ b/src/Magnum/Platform/WindowlessCglApplication.cpp @@ -72,7 +72,7 @@ WindowlessCglContext::WindowlessCglContext(const Configuration& configuration, G Error() << "Platform::WindowlessCglContext: cannot create context"; } -WindowlessCglContext::WindowlessCglContext(WindowlessCglContext&& other): _pixelFormat{other._pixelFormat}, _context{other._context} { +WindowlessCglContext::WindowlessCglContext(WindowlessCglContext&& other) noexcept: _pixelFormat{other._pixelFormat}, _context{other._context} { other._pixelFormat = {}; other._context = {}; } @@ -82,7 +82,7 @@ WindowlessCglContext::~WindowlessCglContext() { if(_pixelFormat) CGLDestroyPixelFormat(_pixelFormat); } -WindowlessCglContext& WindowlessCglContext::operator=(WindowlessCglContext&& other) { +WindowlessCglContext& WindowlessCglContext::operator=(WindowlessCglContext&& other) noexcept { using std::swap; swap(other._pixelFormat, _pixelFormat); swap(other._context, _context); diff --git a/src/Magnum/Platform/WindowlessCglApplication.h b/src/Magnum/Platform/WindowlessCglApplication.h index 0d76c5841..ec2ad98e6 100644 --- a/src/Magnum/Platform/WindowlessCglApplication.h +++ b/src/Magnum/Platform/WindowlessCglApplication.h @@ -98,13 +98,13 @@ class WindowlessCglContext { WindowlessCglContext(const WindowlessCglContext&) = delete; /** @brief Move constructor */ - WindowlessCglContext(WindowlessCglContext&& other); + WindowlessCglContext(WindowlessCglContext&& other) noexcept; /** @brief Copying is not allowed */ WindowlessCglContext& operator=(const WindowlessCglContext&) = delete; /** @brief Move assignment */ - WindowlessCglContext& operator=(WindowlessCglContext&& other); + WindowlessCglContext& operator=(WindowlessCglContext&& other) noexcept; /** * @brief Destructor diff --git a/src/Magnum/Platform/WindowlessEglApplication.cpp b/src/Magnum/Platform/WindowlessEglApplication.cpp index 89fa13406..081004e83 100644 --- a/src/Magnum/Platform/WindowlessEglApplication.cpp +++ b/src/Magnum/Platform/WindowlessEglApplication.cpp @@ -499,7 +499,7 @@ WindowlessEglContext::WindowlessEglContext(const Configuration& configuration, G #endif } -WindowlessEglContext::WindowlessEglContext(WindowlessEglContext&& other): +WindowlessEglContext::WindowlessEglContext(WindowlessEglContext&& other) noexcept: #ifndef MAGNUM_TARGET_WEBGL _sharedContext{other._sharedContext}, #endif @@ -541,7 +541,7 @@ WindowlessEglContext::~WindowlessEglContext() { _display) eglTerminate(_display); } -WindowlessEglContext& WindowlessEglContext::operator=(WindowlessEglContext&& other) { +WindowlessEglContext& WindowlessEglContext::operator=(WindowlessEglContext&& other) noexcept { using std::swap; #ifndef MAGNUM_TARGET_WEBGL swap(other._sharedContext, _sharedContext); diff --git a/src/Magnum/Platform/WindowlessEglApplication.h b/src/Magnum/Platform/WindowlessEglApplication.h index 1eaf0fbe5..2ccc1478f 100644 --- a/src/Magnum/Platform/WindowlessEglApplication.h +++ b/src/Magnum/Platform/WindowlessEglApplication.h @@ -108,13 +108,13 @@ class WindowlessEglContext { WindowlessEglContext(const WindowlessEglContext&) = delete; /** @brief Move constructor */ - WindowlessEglContext(WindowlessEglContext&& other); + WindowlessEglContext(WindowlessEglContext&& other) noexcept; /** @brief Copying is not allowed */ WindowlessEglContext& operator=(const WindowlessEglContext&) = delete; /** @brief Move assignment */ - WindowlessEglContext& operator=(WindowlessEglContext&& other); + WindowlessEglContext& operator=(WindowlessEglContext&& other) noexcept; /** * @brief Destructor diff --git a/src/Magnum/Platform/WindowlessGlxApplication.cpp b/src/Magnum/Platform/WindowlessGlxApplication.cpp index ed0d2d80e..d2b5791e2 100644 --- a/src/Magnum/Platform/WindowlessGlxApplication.cpp +++ b/src/Magnum/Platform/WindowlessGlxApplication.cpp @@ -280,7 +280,7 @@ WindowlessGlxContext::WindowlessGlxContext(const WindowlessGlxContext::Configura } } -WindowlessGlxContext::WindowlessGlxContext(WindowlessGlxContext&& other): _display{other._display}, _pbuffer{other._pbuffer}, _context{other._context} { +WindowlessGlxContext::WindowlessGlxContext(WindowlessGlxContext&& other) noexcept: _display{other._display}, _pbuffer{other._pbuffer}, _context{other._context} { other._display = {}; other._context = {}; other._pbuffer = {}; @@ -292,7 +292,7 @@ WindowlessGlxContext::~WindowlessGlxContext() { if(_display) XCloseDisplay(_display); } -WindowlessGlxContext& WindowlessGlxContext::operator=(WindowlessGlxContext&& other) { +WindowlessGlxContext& WindowlessGlxContext::operator=(WindowlessGlxContext&& other) noexcept { using std::swap; swap(other._display, _display); swap(other._pbuffer, _pbuffer); diff --git a/src/Magnum/Platform/WindowlessGlxApplication.h b/src/Magnum/Platform/WindowlessGlxApplication.h index f549f7d1f..5b3f39906 100644 --- a/src/Magnum/Platform/WindowlessGlxApplication.h +++ b/src/Magnum/Platform/WindowlessGlxApplication.h @@ -119,13 +119,13 @@ class WindowlessGlxContext { WindowlessGlxContext(const WindowlessGlxContext&) = delete; /** @brief Move constructor */ - WindowlessGlxContext(WindowlessGlxContext&& other); + WindowlessGlxContext(WindowlessGlxContext&& other) noexcept; /** @brief Copying is not allowed */ WindowlessGlxContext& operator=(const WindowlessGlxContext&) = delete; /** @brief Move assignment */ - WindowlessGlxContext& operator=(WindowlessGlxContext&& other); + WindowlessGlxContext& operator=(WindowlessGlxContext&& other) noexcept; /** * @brief Destructor diff --git a/src/Magnum/Platform/WindowlessIosApplication.h b/src/Magnum/Platform/WindowlessIosApplication.h index 5932a13b4..40ca1fdd1 100644 --- a/src/Magnum/Platform/WindowlessIosApplication.h +++ b/src/Magnum/Platform/WindowlessIosApplication.h @@ -91,13 +91,13 @@ class WindowlessIosContext { WindowlessIosContext(const WindowlessIosContext&) = delete; /** @brief Move constructor */ - WindowlessIosContext(WindowlessIosContext&& other); + WindowlessIosContext(WindowlessIosContext&& other) noexcept; /** @brief Copying is not allowed */ WindowlessIosContext& operator=(const WindowlessIosContext&) = delete; /** @brief Move assignment */ - WindowlessIosContext& operator=(WindowlessIosContext&& other); + WindowlessIosContext& operator=(WindowlessIosContext&& other) noexcept; /** * @brief Destructor diff --git a/src/Magnum/Platform/WindowlessIosApplication.mm b/src/Magnum/Platform/WindowlessIosApplication.mm index c3778778f..8665f0315 100644 --- a/src/Magnum/Platform/WindowlessIosApplication.mm +++ b/src/Magnum/Platform/WindowlessIosApplication.mm @@ -52,7 +52,7 @@ WindowlessIosContext::WindowlessIosContext(const Configuration&, GLContext*) { } } -WindowlessIosContext::WindowlessIosContext(WindowlessIosContext&& other): _context{other._context} { +WindowlessIosContext::WindowlessIosContext(WindowlessIosContext&& other) noexcept: _context{other._context} { other._context = {}; } @@ -60,7 +60,7 @@ WindowlessIosContext::~WindowlessIosContext() { if(_context) [_context dealloc]; } -WindowlessIosContext& WindowlessIosContext::operator=(WindowlessIosContext&& other) { +WindowlessIosContext& WindowlessIosContext::operator=(WindowlessIosContext&& other) noexcept { using std::swap; swap(other._context, _context); return *this; diff --git a/src/Magnum/Platform/WindowlessWglApplication.cpp b/src/Magnum/Platform/WindowlessWglApplication.cpp index 760c1dcd6..5a3b33b12 100644 --- a/src/Magnum/Platform/WindowlessWglApplication.cpp +++ b/src/Magnum/Platform/WindowlessWglApplication.cpp @@ -266,7 +266,7 @@ WindowlessWglContext::WindowlessWglContext(const Configuration& configuration, G Error() << "Platform::WindowlessWglContext: cannot create context:" << GetLastError(); } -WindowlessWglContext::WindowlessWglContext(WindowlessWglContext&& other): _window{other._window}, _deviceContext{other._deviceContext}, _context{other._context} { +WindowlessWglContext::WindowlessWglContext(WindowlessWglContext&& other) noexcept: _window{other._window}, _deviceContext{other._deviceContext}, _context{other._context} { other._window = {}; other._deviceContext = {}; other._context = {}; @@ -277,7 +277,7 @@ WindowlessWglContext::~WindowlessWglContext() { if(_window) DestroyWindow(_window); } -WindowlessWglContext& WindowlessWglContext::operator=(WindowlessWglContext&& other) { +WindowlessWglContext& WindowlessWglContext::operator=(WindowlessWglContext&& other) noexcept { using std::swap; swap(other._window, _window); swap(other._deviceContext, _deviceContext); diff --git a/src/Magnum/Platform/WindowlessWglApplication.h b/src/Magnum/Platform/WindowlessWglApplication.h index 923b51514..c27cd66c9 100644 --- a/src/Magnum/Platform/WindowlessWglApplication.h +++ b/src/Magnum/Platform/WindowlessWglApplication.h @@ -106,13 +106,13 @@ class WindowlessWglContext { WindowlessWglContext(const WindowlessWglContext&) = delete; /** @brief Move constructor */ - WindowlessWglContext(WindowlessWglContext&& other); + WindowlessWglContext(WindowlessWglContext&& other) noexcept; /** @brief Copying is not allowed */ WindowlessWglContext& operator=(const WindowlessWglContext&) = delete; /** @brief Move assignment */ - WindowlessWglContext& operator=(WindowlessWglContext&& other); + WindowlessWglContext& operator=(WindowlessWglContext&& other) noexcept; /** * @brief Destructor diff --git a/src/Magnum/Platform/WindowlessWindowsEglApplication.cpp b/src/Magnum/Platform/WindowlessWindowsEglApplication.cpp index c959bdbfc..3c16543db 100644 --- a/src/Magnum/Platform/WindowlessWindowsEglApplication.cpp +++ b/src/Magnum/Platform/WindowlessWindowsEglApplication.cpp @@ -163,7 +163,7 @@ WindowlessWindowsEglContext::WindowlessWindowsEglContext(const Configuration& co Error() << "Platform::WindowlessWindowsEglContext: cannot create window surface:" << Implementation::eglErrorString(eglGetError()); } -WindowlessWindowsEglContext::WindowlessWindowsEglContext(WindowlessWindowsEglContext&& other): _window{other._window}, _display{other._display}, _surface{other._surface}, _context{other._context} { +WindowlessWindowsEglContext::WindowlessWindowsEglContext(WindowlessWindowsEglContext&& other) noexcept: _window{other._window}, _display{other._display}, _surface{other._surface}, _context{other._context} { other._window = {}; other._display = {}; other._surface = {}; @@ -177,7 +177,7 @@ WindowlessWindowsEglContext::~WindowlessWindowsEglContext() { if(_window) DestroyWindow(_window); } -WindowlessWindowsEglContext& WindowlessWindowsEglContext::operator=(WindowlessWindowsEglContext&& other) { +WindowlessWindowsEglContext& WindowlessWindowsEglContext::operator=(WindowlessWindowsEglContext&& other) noexcept { using std::swap; swap(other._window, _window); swap(other._display, _display); diff --git a/src/Magnum/Platform/WindowlessWindowsEglApplication.h b/src/Magnum/Platform/WindowlessWindowsEglApplication.h index e0a6df5f8..555ef681a 100644 --- a/src/Magnum/Platform/WindowlessWindowsEglApplication.h +++ b/src/Magnum/Platform/WindowlessWindowsEglApplication.h @@ -91,13 +91,13 @@ class WindowlessWindowsEglContext { WindowlessWindowsEglContext(const WindowlessWindowsEglContext&) = delete; /** @brief Move constructor */ - WindowlessWindowsEglContext(WindowlessWindowsEglContext&& other); + WindowlessWindowsEglContext(WindowlessWindowsEglContext&& other) noexcept; /** @brief Copying is not allowed */ WindowlessWindowsEglContext& operator=(const WindowlessWindowsEglContext&) = delete; /** @brief Move assignment */ - WindowlessWindowsEglContext& operator=(WindowlessWindowsEglContext&& other); + WindowlessWindowsEglContext& operator=(WindowlessWindowsEglContext&& other) noexcept; /** * @brief Destructor From 2f3189142052b5145009cf76cdac9e5aadfed5b4 Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Tue, 15 Jun 2021 09:25:44 +0200 Subject: [PATCH 70/93] Add noexcept here as well. --- src/Magnum/ResourceManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magnum/ResourceManager.h b/src/Magnum/ResourceManager.h index b7f66101b..9315a7dde 100644 --- a/src/Magnum/ResourceManager.h +++ b/src/Magnum/ResourceManager.h @@ -570,7 +570,7 @@ template struct ResourceManagerData::Data { Data(const Data&) = delete; - Data(Data&& other): data(other.data), state(other.state), policy(other.policy), referenceCount(other.referenceCount) { + Data(Data&& other) noexcept: data{other.data}, state{other.state}, policy{other.policy}, referenceCount{other.referenceCount} { other.data = nullptr; other.referenceCount = 0; } From 3102678d19811a735f0da7e294a2acff40ffc6cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 15 Jun 2021 12:02:49 +0200 Subject: [PATCH 71/93] doc: updated credits. --- doc/credits.dox | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/credits.dox b/doc/credits.dox index 9bf8b54ba..14959fef0 100644 --- a/doc/credits.dox +++ b/doc/credits.dox @@ -81,6 +81,8 @@ Big thanks to everyone involved! Are the below lists missing your name or something's wrong? [Let us know!](https://magnum.graphics/contact/) +- **Aaron Gokaslan** ([\@Skylion007](https://github.com/Skylion007)) --- + various minor code modernization - **[\@abgita](https://github.com/abgita)** --- minor typo fixes - **Alan Jefferson** ([\@alanjfs](https://github.com/alanjfs)) --- extensive usability and first-time-use feedback From 93fd6683879b61f3565bb0d9d7b404056a601049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 18 Jun 2021 20:02:42 +0200 Subject: [PATCH 72/93] Shaders: wait, this should work, and not assert. --- src/Magnum/Shaders/Test/PhongGLTest.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 9ab585b29..4a1c18461 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -231,6 +231,8 @@ constexpr struct { {"vertex colors + diffuse texture", PhongGL::Flag::VertexColor|PhongGL::Flag::DiffuseTexture, 1}, #ifndef MAGNUM_TARGET_GLES2 {"object ID", PhongGL::Flag::ObjectId, 1}, + /* This is fine, InstancedObjectId isn't (check in ConstructInvalidData) */ + {"object ID + separate bitangent", PhongGL::Flag::ObjectId|PhongGL::Flag::Bitangent, 1}, {"instanced object ID", PhongGL::Flag::InstancedObjectId, 1}, {"object ID + alpha mask + specular texture", PhongGL::Flag::ObjectId|PhongGL::Flag::AlphaMask|PhongGL::Flag::SpecularTexture, 1}, #endif From c942e3e7641a2845fcfe14f6505f3debdc81cfe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 18 Jun 2021 20:03:26 +0200 Subject: [PATCH 73/93] Shaders: Bitangent is disallowed only with InstancedObjectId. With just ObjectId it's fine. Sorry for a silly breakage! --- src/Magnum/Shaders/PhongGL.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Magnum/Shaders/PhongGL.cpp b/src/Magnum/Shaders/PhongGL.cpp index 57723011f..3cd3a1aa0 100644 --- a/src/Magnum/Shaders/PhongGL.cpp +++ b/src/Magnum/Shaders/PhongGL.cpp @@ -91,7 +91,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount "Shaders::PhongGL: texture transformation enabled but the shader is not textured", ); #ifndef MAGNUM_TARGET_GLES2 - CORRADE_ASSERT(!(flags & Flag::InstancedObjectId) || !(flags & Flag::Bitangent), + CORRADE_ASSERT(!(flags >= Flag::InstancedObjectId) || !(flags & Flag::Bitangent), "Shaders::PhongGL: Bitangent attribute binding conflicts with the ObjectId attribute, use a Tangent4 attribute with instanced object ID rendering instead", ); #endif From 202dff63aec20905591c3d574ea6e637d9f9f3ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 18 Jun 2021 21:23:39 +0200 Subject: [PATCH 74/93] package/ci: MSYS, the ancient curses seem to have returned. --- package/ci/appveyor-lcov.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package/ci/appveyor-lcov.sh b/package/ci/appveyor-lcov.sh index 1ffaac39c..be9bca756 100644 --- a/package/ci/appveyor-lcov.sh +++ b/package/ci/appveyor-lcov.sh @@ -9,7 +9,10 @@ curl -O http://repo.msys2.org/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.ta pacman-key --verify msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig pacman -U --noconfirm msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz -pacman -Sy --noconfirm mingw-w64-x86_64-perl +# Newer packages use zstd, but this old pacman has no idea what that is. +# Download the last perl that's still compressed with xz. +curl -O http://repo.msys2.org/msys/x86_64/perl-5.30.2-1-x86_64.pkg.tar.xz +pacman -U --noconfirm perl-5.30.2-1-x86_64.pkg.tar.xz # mingw lcov package is empty, so download and use it manually # https://github.com/appveyor/ci/issues/1628 From f216320da6a89987f587445b80b384432d8b7de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 22 Jun 2021 15:09:16 +0200 Subject: [PATCH 75/93] GCC 11, your fancy new warnings are NOT helpful. --- doc/snippets/MagnumGL.cpp | 8 ++++++++ doc/snippets/MagnumMath.cpp | 16 ++++++++++++++++ src/Magnum/Trade/Test/MeshDataTest.cpp | 12 ++++++------ src/Magnum/Trade/Test/SkinDataTest.cpp | 4 ++-- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/doc/snippets/MagnumGL.cpp b/doc/snippets/MagnumGL.cpp index 1a4baa4ea..e11b93612 100644 --- a/doc/snippets/MagnumGL.cpp +++ b/doc/snippets/MagnumGL.cpp @@ -121,7 +121,15 @@ carBumpTexture.setStorage(5, GL::TextureFormat::RGB8, {256, 256}) #endif { +#if defined(CORRADE_TARGET_GCC) && __GNUC__ >= 11 +#pragma GCC diagnostic push +/* Stupid thing. YES I WANT THIS TO BE A FUNCTION, CAN YOU SHUT UP */ +#pragma GCC diagnostic ignored "-Wvexing-parse" +#endif auto importSomeMesh() -> std::tuple; +#if defined(CORRADE_TARGET_GCC) && __GNUC__ >= 11 +#pragma GCC diagnostic pop +#endif /* [opengl-wrapping-nocreate] */ GL::Mesh mesh{NoCreate}; GL::Buffer vertices{NoCreate}, indices{NoCreate}; diff --git a/doc/snippets/MagnumMath.cpp b/doc/snippets/MagnumMath.cpp index 81ebbeab8..9ecdd54f2 100644 --- a/doc/snippets/MagnumMath.cpp +++ b/doc/snippets/MagnumMath.cpp @@ -625,6 +625,11 @@ static_cast(radians); } { +#if defined(CORRADE_TARGET_GCC) && __GNUC__ >= 11 +#pragma GCC diagnostic push +/* Stupid thing. YES I WANT THIS TO BE A FUNCTION, CAN YOU SHUT UP */ +#pragma GCC diagnostic ignored "-Wvexing-parse" +#endif /* [Deg-usage-convert] */ Double foo(); @@ -634,6 +639,9 @@ Radd radians{foo()}; /* [Deg-usage-convert] */ static_cast(degrees); static_cast(radians); +#if defined(CORRADE_TARGET_GCC) && __GNUC__ >= 11 +#pragma GCC diagnostic pop +#endif } { @@ -646,6 +654,11 @@ static_cast(b); } { +#if defined(CORRADE_TARGET_GCC) && __GNUC__ >= 11 +#pragma GCC diagnostic push +/* Stupid thing. YES I WANT THIS TO BE A FUNCTION, CAN YOU SHUT UP */ +#pragma GCC diagnostic ignored "-Wvexing-parse" +#endif Double foo(); /* [Deg-usage-comparison] */ Rad angle(); @@ -654,6 +667,9 @@ Deg x = angle(); // convert to degrees for easier comparison if(x < 30.0_degf) foo(); //if(x > 1.57_radf) bar(); // error, both need to be of the same type /* [Deg-usage-comparison] */ +#if defined(CORRADE_TARGET_GCC) && __GNUC__ >= 11 +#pragma GCC diagnostic pop +#endif } { diff --git a/src/Magnum/Trade/Test/MeshDataTest.cpp b/src/Magnum/Trade/Test/MeshDataTest.cpp index 4f7deecb2..ce9bbfa0f 100644 --- a/src/Magnum/Trade/Test/MeshDataTest.cpp +++ b/src/Magnum/Trade/Test/MeshDataTest.cpp @@ -1552,8 +1552,8 @@ void MeshDataTest::constructAttributelessNotOwned() { } void MeshDataTest::constructIndexlessAttributeless() { - int importerState; - MeshData data{MeshPrimitive::TriangleStrip, 37, &importerState}; + int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ + MeshData data{MeshPrimitive::TriangleStrip, 37, &state}; /* These are both empty so it doesn't matter, but this is a nice non-restrictive default */ CORRADE_COMPARE(data.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable); @@ -1562,7 +1562,7 @@ void MeshDataTest::constructIndexlessAttributeless() { CORRADE_VERIFY(!data.attributeData()); CORRADE_COMPARE(data.indexData(), nullptr); CORRADE_COMPARE(data.vertexData(), nullptr); - CORRADE_COMPARE(data.importerState(), &importerState); + CORRADE_COMPARE(data.importerState(), &state); CORRADE_VERIFY(!data.isIndexed()); CORRADE_COMPARE(data.vertexCount(), 37); @@ -1570,13 +1570,13 @@ void MeshDataTest::constructIndexlessAttributeless() { } void MeshDataTest::constructIndexlessAttributelessZeroVertices() { - int importerState; - MeshData data{MeshPrimitive::TriangleStrip, 0, &importerState}; + int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ + MeshData data{MeshPrimitive::TriangleStrip, 0, &state}; CORRADE_COMPARE(data.primitive(), MeshPrimitive::TriangleStrip); CORRADE_VERIFY(!data.attributeData()); CORRADE_COMPARE(data.indexData(), nullptr); CORRADE_COMPARE(data.vertexData(), nullptr); - CORRADE_COMPARE(data.importerState(), &importerState); + CORRADE_COMPARE(data.importerState(), &state); CORRADE_VERIFY(!data.isIndexed()); CORRADE_COMPARE(data.vertexCount(), 0); diff --git a/src/Magnum/Trade/Test/SkinDataTest.cpp b/src/Magnum/Trade/Test/SkinDataTest.cpp index f7deb7780..35fed740a 100644 --- a/src/Magnum/Trade/Test/SkinDataTest.cpp +++ b/src/Magnum/Trade/Test/SkinDataTest.cpp @@ -56,7 +56,7 @@ SkinDataTest::SkinDataTest() { } void SkinDataTest::construct() { - int state; + int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ SkinData3D data{{0, 2, 3}, { Matrix4::translation(Vector3::zAxis(0.0f)), Matrix4::translation(Vector3::zAxis(2.0f)), @@ -102,7 +102,7 @@ void SkinDataTest::constructCopy() { } void SkinDataTest::constructMove() { - int state; + int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ SkinData3D a{{0, 2, 3}, { Matrix4::translation(Vector3::zAxis(0.0f)), Matrix4::translation(Vector3::zAxis(2.0f)), From 7346dc2d0187ad1c484d26ada03fddae76f4a657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 22 Jun 2021 15:09:36 +0200 Subject: [PATCH 76/93] package/ci: move a comment to where the macOS image is defined. --- package/ci/circleci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package/ci/circleci.yml b/package/ci/circleci.yml index 67c40fbb2..0a7e6b3ae 100644 --- a/package/ci/circleci.yml +++ b/package/ci/circleci.yml @@ -17,6 +17,9 @@ executors: docker: - image: ubuntu:bionic-20200921 xcode-11_2: + # Molten-vk isn't in (non-updated) Homebrew on the 9.4 or 10.0/1/2 image, + # have to use 10.3 instead; since 2021-06-08 it refuses to work on 10.14 so + # have to use 11.2 at least macos: xcode: 11.2.1 xcode-11_6: @@ -391,9 +394,6 @@ jobs: script: unix-desktop.sh macos-gl: - # Molten-vk isn't in (non-updated) Homebrew on the 9.4 or 10.0/1/2 image, - # have to use 10.3 instead; since 2021-06-08 it refuses to work on 10.14 so - # have to use 11.2 at least executor: xcode-11_2 environment: CMAKE_CXX_FLAGS: --coverage From ffdbae59741587c242ebcda3d28b246987f38f2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 22 Jun 2021 15:48:46 +0200 Subject: [PATCH 77/93] ObjImporter: fix a GCC 11 warning. Ew, this code is terrible. --- src/MagnumPlugins/ObjImporter/ObjImporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MagnumPlugins/ObjImporter/ObjImporter.cpp b/src/MagnumPlugins/ObjImporter/ObjImporter.cpp index 0fc084ff1..18cae9762 100644 --- a/src/MagnumPlugins/ObjImporter/ObjImporter.cpp +++ b/src/MagnumPlugins/ObjImporter/ObjImporter.cpp @@ -189,7 +189,7 @@ void ObjImporter::parseMeshNames() { /* Index data, just mark that we found something for first unnamed object */ - } else if(thisIsFirstMeshAndItHasNoData) for(const std::string& data: {"p", "l", "f"}) { + } else if(thisIsFirstMeshAndItHasNoData) for(const std::string data: {"p", "l", "f"}) { if(keyword == data) { thisIsFirstMeshAndItHasNoData = false; break; From ca677c69fe9ec12ce4df57e01a083695bf938d67 Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Thu, 17 Jun 2021 11:30:59 -0400 Subject: [PATCH 78/93] Fix typos --- CMakeLists.txt | 2 +- doc/building.dox | 2 +- doc/changelog-old.dox | 2 +- doc/changelog.dox | 8 ++--- doc/coding-style.dox | 4 +-- doc/credits.dox | 4 +-- doc/custom-buildsystems.dox | 2 +- doc/debug-tools.dox | 2 +- doc/developers.dox | 12 ++++---- doc/file-formats.dox | 2 +- doc/platforms-android.dox | 4 +-- doc/platforms-html5.dox | 2 +- doc/platforms-windows.dox | 2 +- doc/snippets/CMakeLists.txt | 2 +- doc/transformations.dox | 2 +- modules/FindCorrade.cmake | 2 +- modules/FindMagnum.cmake | 2 +- src/Magnum/Animation/Test/PlayerTest.cpp | 2 +- src/Magnum/Audio/Renderer.h | 2 +- src/Magnum/GL/AbstractShaderProgram.h | 2 +- src/Magnum/GL/Attribute.h | 2 +- src/Magnum/GL/Buffer.h | 2 +- src/Magnum/GL/CMakeLists.txt | 2 +- src/Magnum/GL/Extensions.h | 2 +- src/Magnum/GL/Mesh.h | 4 +-- src/Magnum/GL/RenderbufferFormat.h | 2 +- src/Magnum/GL/Renderer.h | 2 +- src/Magnum/GL/Test/ContextTest.cpp | 2 +- src/Magnum/Math/DualComplex.h | 4 +-- src/Magnum/MeshTools/RemoveDuplicates.cpp | 4 +-- src/Magnum/MeshTools/Tipsify.cpp | 2 +- src/Magnum/Platform/AndroidApplication.h | 4 +-- src/Magnum/Platform/EmscriptenApplication.cpp | 4 +-- src/Magnum/Platform/EmscriptenApplication.h | 2 +- src/Magnum/Platform/GlfwApplication.cpp | 2 +- src/Magnum/Platform/GlfwApplication.h | 2 +- src/Magnum/Platform/Sdl2Application.cpp | 4 +-- src/Magnum/Platform/Sdl2Application.h | 10 +++---- src/Magnum/Primitives/Test/GradientTest.cpp | 4 +-- src/Magnum/SceneGraph/Object.hpp | 10 +++---- src/Magnum/SceneGraph/Test/AnimableTest.cpp | 2 +- src/Magnum/Shaders/FlatGL.h | 2 +- src/Magnum/Shaders/MeshVisualizerGL.h | 2 +- src/Magnum/Shaders/PhongGL.h | 2 +- src/Magnum/Shaders/Test/PhongGLTest.cpp | 2 +- src/Magnum/Text/Renderer.cpp | 2 +- src/Magnum/Trade/AbstractImporter.h | 30 +++++++++---------- src/Magnum/Trade/MeshData.h | 2 +- src/Magnum/Trade/PbrClearCoatMaterialData.h | 2 +- src/Magnum/Vk/Device.h | 2 +- src/Magnum/Vk/Instance.h | 2 +- src/Magnum/Vk/MeshLayout.h | 2 +- src/Magnum/Vk/ShaderSet.cpp | 2 +- src/Magnum/Vk/Test/ExtensionsTest.cpp | 4 +-- src/Magnum/Vk/Test/MeshVkTest.cpp | 2 +- 55 files changed, 96 insertions(+), 96 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a27d12722..7cdef0fd0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -402,7 +402,7 @@ set(MAGNUM_PLUGINS_AUDIOIMPORTER_DEBUG_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBU set(MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}/audioimporters) set(MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}/audioimporters) -# Plugin dirs based on wheter we are in debug or release build, needed by some +# Plugin dirs based on whether we are in debug or release build, needed by some # command-line tools if(CORRADE_TARGET_WINDOWS) set(MAGNUM_PLUGINS_DEBUG_DIR_INIT ${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}) diff --git a/doc/building.dox b/doc/building.dox index a2baba56e..13f942db0 100644 --- a/doc/building.dox +++ b/doc/building.dox @@ -214,7 +214,7 @@ There are also ArchLinux packages for @ref building-plugins-packages-arch "Magnu @subsection building-packages-msys MSYS2 packages [MSYS2](https://www.msys2.org/) package for the latest stable release is -mantained in the official repos. Installing is as simple as this: +maintained in the official repos. Installing is as simple as this: @code{.sh} pacman -S mingw-w64-x86_64-magnum # or mingw-w64-i686-magnum diff --git a/doc/changelog-old.dox b/doc/changelog-old.dox index 1c549b7bd..819039aef 100644 --- a/doc/changelog-old.dox +++ b/doc/changelog-old.dox @@ -1470,7 +1470,7 @@ for a high-level overview. - Support for @gl_extension{ARB,vertex_type_10f_11f_11f_rev} in @cpp AbstractShaderProgram::Attribute::DataType @ce - New variants of @cpp Shader::compile() @ce and @cpp AbstractShaderProgram::link() @ce, - allowing the driver to perform paralell compilation in multiple threads. + allowing the driver to perform parallel compilation in multiple threads. - Added @cpp *Texture::maxSize() @ce queries - @ref MeshTools::compile() for automagic creation of 2D and 3D meshes from imported data diff --git a/doc/changelog.dox b/doc/changelog.dox index 10e6d9f30..97eebaf71 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -796,7 +796,7 @@ Released 2020-06-27, tagged as @cpp "intel-windows-crazy-broken-vao-dsa" @ce workarounds for Intel Windows drivers, disabling @gl_extension{ARB,direct_state_access} code paths in everything releated to buffers and meshes. There are several - issues occuring only in heavier apps, impossible to track down and + issues occurring only in heavier apps, impossible to track down and reproduce in a controlled environment. These two replace the previous @cpp "intel-windows-buggy-dsa-bufferdata-for-index-buffers" @ce workaround that attempted to fix this by doing an explicit buffer binding in some @@ -1111,7 +1111,7 @@ Released 2020-06-27, tagged as [mosra/magnum#401](https://github.com/mosra/magnum/pull/401)) - HiDPI capability checking on macOS and iOS got fixed to behave correctly when the capability is explicitly set to @cb{.xml} @ce in the - `Info.plist` file and additionally, unless overriden, it's assumed to be + `Info.plist` file and additionally, unless overridden, it's assumed to be enabled by default on macOS 10.15+ and iOS 13+. Applications running on these platforms no longer need to supply a custom `Info.plist` in order to enable HiDPI. See @ref platforms-macos-hidpi for more information. @@ -1428,7 +1428,7 @@ Released 2020-06-27, tagged as was deprecated in 2019.10 is now removed. Usually a deprecated feature is kept for at least a year before removal, but in this case it was severely limiting multithreaded applications and removing it was necessary. -- Locations of generic shader attributes was changed in order to accomodate +- Locations of generic shader attributes was changed in order to accommodate for new attributes and use cases. This may break custom shaders if these rely on generic attribute definitions or are used together with @ref MeshTools::compile(). To avoid further breakages you're advised to @@ -1548,7 +1548,7 @@ Released 2019-10-24, tagged as - @ref DebugTools::CompareImage and @ref DebugTools::CompareImageToFile now accept also @ref Corrade::Containers::StridedArrayView2D on the left side of the comparison for added flexibility. See - @ref DebugTools-CompareImage-pixels for more infromation. + @ref DebugTools-CompareImage-pixels for more information. @subsubsection changelog-2019-10-new-gl GL library diff --git a/doc/coding-style.dox b/doc/coding-style.dox index a64610807..157f71b1b 100644 --- a/doc/coding-style.dox +++ b/doc/coding-style.dox @@ -70,7 +70,7 @@ removing redundant prefixes) is encouraged. When a namespace has classes which are commonly forward-declared, consider making a forward declaration header --- it should have the same name as the -namespace itself and contain foward declarations for all classes, enums and +namespace itself and contain forward declarations for all classes, enums and copies of all meaningful typedefs. See @ref compilation-forward-declarations for more information. @@ -312,7 +312,7 @@ CORRADE_COMPARE(b, Vector2(1, 0)); CORRADE_VERIFY(!(std::is_convertible::value)); @endcode -If some type should be constructible also from base type (additionaly to copy +If some type should be constructible also from base type (additionally to copy constructor), don't forget to test that too. The test is also usually needed only for low-level frequently used types (vectors, matrices) where such error would do largest harm. Depending on how copy constructor is implemented, you diff --git a/doc/credits.dox b/doc/credits.dox index 14959fef0..257920337 100644 --- a/doc/credits.dox +++ b/doc/credits.dox @@ -39,7 +39,7 @@ overview the dependencies are color-coded: no license requirements, such as usage of public APIs like Vulkan or OpenAL or usage of platform-specific interfaces. - A @m_class{m-label m-success} **green** label marks licenses that make the - dependency safe to use in a commerical setting without having to release + dependency safe to use in a commercial setting without having to release your source code, usually requiring you to give attribution. - A @m_class{m-label m-primary} **light blue** label marks public domain software. In some countries there's no notion of public domain, in which @@ -212,7 +212,7 @@ Are the below lists missing your name or something's wrong? - **Thibault Jochem** ([\@Tryum](https://github.com/Tryum)) --- @ref Platform::GlfwApplication improvements - **Thomas Tissot-Dupont** ([\@dolphineye](https://github.com/dolphineye)) - --- OpenGL ES compatiblity improvements + --- OpenGL ES compatibility improvements - **Travis Watkins** ([\@amaranth](https://github.com/amaranth)) --- support for windowless applications under macOS */ diff --git a/doc/custom-buildsystems.dox b/doc/custom-buildsystems.dox index fba069895..0d907e2ec 100644 --- a/doc/custom-buildsystems.dox +++ b/doc/custom-buildsystems.dox @@ -84,7 +84,7 @@ more information. While dynamic plugins work without buildsystem integration, static plugins are handled automagically with CMake and you need to replicate the magic manually -when using a custom buildsystem. This is just about compiling an additonal +when using a custom buildsystem. This is just about compiling an additional `*.cpp` file together with your final app, see @ref plugins-static for more information. diff --git a/doc/debug-tools.dox b/doc/debug-tools.dox index c1b2f0b2a..4481059f1 100644 --- a/doc/debug-tools.dox +++ b/doc/debug-tools.dox @@ -46,7 +46,7 @@ of them to any object. Basic usage involves instancing @ref DebugTools::ResourceManager and keeping it for the whole lifetime of debug renderers. Next you need some @ref SceneGraph::DrawableGroup instance. You can use the same group as for the -rest of your scene, but preferrably use dedicated one for debug renderers, so +rest of your scene, but preferably use dedicated one for debug renderers, so you can easily enable or disable debug rendering. Next step is to create configuration for your debug renderers and create diff --git a/doc/developers.dox b/doc/developers.dox index 3c8ccd731..5f0af2f54 100644 --- a/doc/developers.dox +++ b/doc/developers.dox @@ -634,7 +634,7 @@ unless it doesn't affect public API at all. capabilities on the same platform (DSA / non-DSA is one of them), add separate code paths: - new private and `MAGNUM_LOCAL` `thingImplementation*()` functions, each - implementing one code path, preferrably @cpp static @ce + implementing one code path, preferably @cpp static @ce - a `thingImplementation` (member) function pointer in `src/Magnum/Implementation/SomeState.h` that gets populated in `src/Magnum/GL/Implementation/SomeState.cpp` based on extension / version availability @@ -683,7 +683,7 @@ unless it doesn't affect public API at all. `KnownWorkarounds` array in `src/Magnum/GL/Implementation/driverSpecific.cpp`, ideally reuse some of the already existing vendor prefixes 2. If the workaround can be tied down to a particular platform / target, add - apropriate @cpp #ifdef @ce around it + appropriate @cpp #ifdef @ce around it 3. Create (or extend) a pair of (member) @cpp private @ce `MAGNUM_LOCAL` `*Implementation*()` functions in the affected class, `*ImplementationDefault()` having the optimistic default behavior, the other having the workaround. @@ -706,7 +706,7 @@ unless it doesn't affect public API at all. @ref opengl-workarounds Removeing a workaround is simply a matter of searching for its string, removing -all occurences of that string and removing all `*Implementation*()` functions +all occurrences of that string and removing all `*Implementation*()` functions that were used only if the workaround was in place. No need to deprecate anything, users explicitly disabling given workaround will only be informed that such workaround does not exist anymore. @@ -992,7 +992,7 @@ inverse. replace `... —` with `..., 20XZ —` there as well 4. Copy all `Find*.cmake` modules to dependent projects to update the copyright year in these as well -5. Update other occurences by hand: +5. Update other occurrences by hand: - `package/debian/copyright` - `doc/conf.py` - All flextGL `*.template` files in `src/external/OpenGL` and @@ -1061,7 +1061,7 @@ inverse. - add a temporary \@anchor changelog-latest (and equivalent in other repos) on top so the links from main page work properly -8. Convert all occurences of +8. Convert all occurrences of - \@m_since_latest_{thing} to \@m_since_{thing,20XY,ab} - \@m_since_latest to \@m_since{20XY,ab} - \@m_deprecated_since_latest_{thing} to @@ -1103,7 +1103,7 @@ inverse. new functionality - add release annoucement link under the button on front page 21. Publish the release announcement, verify it looks correct -22. Advertise the release announcement, preferrably Monday 5 PM, never Friday +22. Advertise the release announcement, preferably Monday 5 PM, never Friday or weekends - come up with some 100-character-long extended title - Twitter (extended title + url and some hashtags), first dry-run the diff --git a/doc/file-formats.dox b/doc/file-formats.dox index fef30737c..08d77c8d1 100644 --- a/doc/file-formats.dox +++ b/doc/file-formats.dox @@ -611,7 +611,7 @@ mentioning requirements coming from the license: dependencies, which don't require anything from you in order to use them and put no restrictions on use - @m_class{m-label m-success} **green** marks licenses that make the - dependency safe to use in a commerical setting without having to release + dependency safe to use in a commercial setting without having to release your source code, usually requiring you to give attribution. - @m_class{m-label m-warning} **yellow** marks licenses that require you to either dynamically link to the software to be able to use it in a diff --git a/doc/platforms-android.dox b/doc/platforms-android.dox index 5be3d9997..ff75adf55 100644 --- a/doc/platforms-android.dox +++ b/doc/platforms-android.dox @@ -250,7 +250,7 @@ endif() @endcode On the first run, the macro will attempt to detect SDK location, Android Build -Tools version and Android Platfrom version and it prints them to the output +Tools version and Android Platform version and it prints them to the output like this: @code{.shell-session} @@ -1137,7 +1137,7 @@ This for 3.3.0: @endcode And probably a dozen other variants, but I am not willing to waste my time any -further by enumerating all possible embarrasing crashes of cursed tools. The +further by enumerating all possible embarrassing crashes of cursed tools. The oldest Android Gradle plugin version that worked with Gradle 6.6.1 and NDK r19 was 3.6.0, and to change it update the following entry in your `build.gradle`: diff --git a/doc/platforms-html5.dox b/doc/platforms-html5.dox index 2714c4a75..0f348206f 100644 --- a/doc/platforms-html5.dox +++ b/doc/platforms-html5.dox @@ -379,7 +379,7 @@ using a @cb{.css} padding-bottom @ce style with a percentage equal to inverse of the ratio for @cb{.css} div#expander @ce --- for example, a 2.35:1 ratio would be @cb{.html}
    @ce. -Size of the canvas can be also overriden by specifying one of the +Size of the canvas can be also overridden by specifying one of the @cb{.css} .mn-width-* @ce CSS classes on the @cb{.html}
    @ce: diff --git a/doc/platforms-windows.dox b/doc/platforms-windows.dox index c5d5dc934..704b3ecfa 100644 --- a/doc/platforms-windows.dox +++ b/doc/platforms-windows.dox @@ -128,7 +128,7 @@ much as possible: @section platform-windows-icon Executable icon In order to supply an icon for the executable, make an `*.ico` file -(preferrably out of multiple different sizes) and create a `*.rc` file +(preferably out of multiple different sizes) and create a `*.rc` file referencing it. The first argument can be anything (it can be used for retrieving the icon later at runtime via Windows APIs), Windows always pick the first icon in the `*.rc` file for the executable. diff --git a/doc/snippets/CMakeLists.txt b/doc/snippets/CMakeLists.txt index dd8510fd4..9af7aca4b 100644 --- a/doc/snippets/CMakeLists.txt +++ b/doc/snippets/CMakeLists.txt @@ -141,7 +141,7 @@ if(WITH_DEBUGTOOLS) # CompareImage documentation snippet. I need it executable so I can # copy&paste the output to the documentation. Also not using # corrade_add_test() because it shouldn't be run as part of CTest as it - # purposedly fails. + # purposely fails. add_executable(debugtools-compareimage debugtools-compareimage.cpp) target_link_libraries(debugtools-compareimage PRIVATE MagnumDebugTools diff --git a/doc/transformations.dox b/doc/transformations.dox index b1ad714f3..7c389596d 100644 --- a/doc/transformations.dox +++ b/doc/transformations.dox @@ -319,7 +319,7 @@ create helper functions, if you need them. The @ref SceneGraph API provides a hierarchical transformation hierarchy and a correct camera-relative transformation is calculated automatically in the -background, avoiding the need for manualy handling of model / view +background, avoiding the need for manually handling of model / view transformations. In particular, camera position is always specified as relative to scene root and it gets inverted when calculating the final per-object transformation. See @ref scenegraph for detailed description. diff --git a/modules/FindCorrade.cmake b/modules/FindCorrade.cmake index 9a36cce28..39e7cd4b7 100644 --- a/modules/FindCorrade.cmake +++ b/modules/FindCorrade.cmake @@ -100,7 +100,7 @@ # CORRADE_TARGET_MINGW - Defined if compiling under MinGW # CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT - Defined if PluginManager # doesn't support dynamic plugin loading due to platform limitations -# CORRADE_TESTSUITE_TARGET_XCTEST - Defined if TestSuite is targetting Xcode +# CORRADE_TESTSUITE_TARGET_XCTEST - Defined if TestSuite is targeting Xcode # XCTest # CORRADE_UTILITY_USE_ANSI_COLORS - Defined if ANSI escape sequences are used # for colored output with Utility::Debug on Windows diff --git a/modules/FindMagnum.cmake b/modules/FindMagnum.cmake index 1c150aa05..e1fb257b2 100644 --- a/modules/FindMagnum.cmake +++ b/modules/FindMagnum.cmake @@ -608,7 +608,7 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) # Dynamic plugins don't have any prefix (e.g. `lib` on Linux), # search with empty prefix and then reset that back so we don't - # accidentaly break something else + # accidentally break something else set(_tmp_prefixes "${CMAKE_FIND_LIBRARY_PREFIXES}") set(CMAKE_FIND_LIBRARY_PREFIXES "${CMAKE_FIND_LIBRARY_PREFIXES};") diff --git a/src/Magnum/Animation/Test/PlayerTest.cpp b/src/Magnum/Animation/Test/PlayerTest.cpp index 2d8c1eba4..10199165b 100644 --- a/src/Magnum/Animation/Test/PlayerTest.cpp +++ b/src/Magnum/Animation/Test/PlayerTest.cpp @@ -120,7 +120,7 @@ const struct { initializer (not when there's just a struct and also not when the struct is only a chrono member itself). Keeping at least one instance here so I can monitor when this gets fixed. */ - #if !defined(CORRADE_MSVC2017_COMPATIBILITY) || defined(CORRADE_MSVC2015_COMPATIBLITY) + #if !defined(CORRADE_MSVC2017_COMPATIBILITY) || defined(CORRADE_MSVC2015_COMPATIBILITY) std::chrono::hours{100*365*24}, #else std::chrono::minutes{100*365*24*60}, diff --git a/src/Magnum/Audio/Renderer.h b/src/Magnum/Audio/Renderer.h index a61c88114..3308019cb 100644 --- a/src/Magnum/Audio/Renderer.h +++ b/src/Magnum/Audio/Renderer.h @@ -50,7 +50,7 @@ class Renderer { * @see @ref error() */ enum class Error: ALenum { - NoError = AL_NO_ERROR, /**< No error occured */ + NoError = AL_NO_ERROR, /**< No error occurred */ InvalidName = AL_INVALID_NAME, /**< Invalid name parameter */ InvalidEnum = AL_INVALID_ENUM, /**< Invalid enum parameter */ InvalidValue = AL_INVALID_VALUE, /**< Invalid enum value parameter */ diff --git a/src/Magnum/GL/AbstractShaderProgram.h b/src/Magnum/GL/AbstractShaderProgram.h index 1af6737eb..ab23a5156 100644 --- a/src/Magnum/GL/AbstractShaderProgram.h +++ b/src/Magnum/GL/AbstractShaderProgram.h @@ -111,7 +111,7 @@ layout(location = 1) in vec3 normal; layout(location = 2) in vec2 textureCoordinates; @endcode -Similarly for ouput attributes, you can also specify blend equation color index +Similarly for output attributes, you can also specify blend equation color index for them (see @ref Renderer::BlendFunction for more information about using color input index): diff --git a/src/Magnum/GL/Attribute.h b/src/Magnum/GL/Attribute.h index 8a6ae43fe..1b27fc848 100644 --- a/src/Magnum/GL/Attribute.h +++ b/src/Magnum/GL/Attribute.h @@ -360,7 +360,7 @@ template class Attribute { * * Used for describing matrix attributes. Implicitly the same as size * of given vector type (e.g. @cpp 9 @ce for a - * @ref Magnum::Matrix3 "Matrix3"), but can be overriden for example to + * @ref Magnum::Matrix3 "Matrix3"), but can be overridden for example to * ensure four-byte column alignment with 1- and 2-byte data types. * @see @ref Vectors */ diff --git a/src/Magnum/GL/Buffer.h b/src/Magnum/GL/Buffer.h index 041099796..1e9baaba4 100644 --- a/src/Magnum/GL/Buffer.h +++ b/src/Magnum/GL/Buffer.h @@ -477,7 +477,7 @@ class MAGNUM_GL_EXPORT Buffer: public AbstractObject { /** * Only one or more discrete subranges of the mapping will be * modified. See @ref flushMappedRange() for more information. May - * only be used in conjuction with @ref MapFlag::Write. + * only be used in conjunction with @ref MapFlag::Write. */ #ifndef MAGNUM_TARGET_GLES2 FlushExplicit = GL_MAP_FLUSH_EXPLICIT_BIT, diff --git a/src/Magnum/GL/CMakeLists.txt b/src/Magnum/GL/CMakeLists.txt index 935e8c524..6933702e8 100644 --- a/src/Magnum/GL/CMakeLists.txt +++ b/src/Magnum/GL/CMakeLists.txt @@ -176,7 +176,7 @@ add_library(MagnumGLObjects OBJECT ${MagnumGL_HEADERS} ${MagnumGL_PRIVATE_HEADERS}) # We can use both implicit include path (GLES2/gl2.h) where our headers can -# be overriden with system ones or explicit (MagnumExternal/OpenGL/GLES2/gl2ext.h) +# be overridden with system ones or explicit (MagnumExternal/OpenGL/GLES2/gl2ext.h) # where only our headers will be used target_include_directories(MagnumGLObjects PUBLIC $) diff --git a/src/Magnum/GL/Extensions.h b/src/Magnum/GL/Extensions.h index 92db6ce13..321dd9b0b 100644 --- a/src/Magnum/GL/Extensions.h +++ b/src/Magnum/GL/Extensions.h @@ -35,7 +35,7 @@ namespace Magnum { namespace GL { /* Standard Android build system thinks that it's okay to define unmangled unprefixed macros. I think that whoever did that needs to be punished, - becuase I am then not able to use that identifier for extension names. + because I am then not able to use that identifier for extension names. Use CORRADE_TARGET_ANDROID here instead. */ #ifdef ANDROID #undef ANDROID diff --git a/src/Magnum/GL/Mesh.h b/src/Magnum/GL/Mesh.h index ce5f329f5..7b77a0e4e 100644 --- a/src/Magnum/GL/Mesh.h +++ b/src/Magnum/GL/Mesh.h @@ -275,7 +275,7 @@ this: @snippet MagnumGL.cpp Mesh-dynamic -@section GL-Mesh-buffer-ownership Transfering buffer ownership +@section GL-Mesh-buffer-ownership Transferring buffer ownership If a vertex/index buffer is used only by a single mesh, it's possible to transfer its ownership to the mesh itself to simplify resource management on @@ -298,7 +298,7 @@ getting only a moved-out instance. For example: Basic workflow is: bind specific framebuffer for drawing (if needed), set up respective shader (see @ref GL-AbstractShaderProgram-rendering-workflow "AbstractShaderProgram documentation" -for more infromation) and call @ref AbstractShaderProgram::draw(). +for more information) and call @ref AbstractShaderProgram::draw(). @section GL-Mesh-webgl-restrictions WebGL restrictions diff --git a/src/Magnum/GL/RenderbufferFormat.h b/src/Magnum/GL/RenderbufferFormat.h index 0d037d79c..0a0a3182a 100644 --- a/src/Magnum/GL/RenderbufferFormat.h +++ b/src/Magnum/GL/RenderbufferFormat.h @@ -114,7 +114,7 @@ enum class RenderbufferFormat: GLenum { * RGBA, each component normalized unsigned byte. * @requires_gles30 Extension @gl_extension{ARM,rgba8} or @gl_extension{OES,rgb8_rgba8} * in OpenGL ES 2.0. - * @requires_webgl20 Not availabe in WebGL 1.0, use for example + * @requires_webgl20 Not available in WebGL 1.0, use for example * @ref RenderbufferFormat::RGB565 or @ref RenderbufferFormat::RGBA4 * instead. */ diff --git a/src/Magnum/GL/Renderer.h b/src/Magnum/GL/Renderer.h index ec7433992..8f1a71fe8 100644 --- a/src/Magnum/GL/Renderer.h +++ b/src/Magnum/GL/Renderer.h @@ -2114,7 +2114,7 @@ class MAGNUM_GL_EXPORT Renderer { * WebGL. */ enum class GraphicsResetStatus: GLenum { - /** No reset occured since last call. */ + /** No reset occurred since last call. */ NoError = GL_NO_ERROR, /** diff --git a/src/Magnum/GL/Test/ContextTest.cpp b/src/Magnum/GL/Test/ContextTest.cpp index afcba1fff..cbdd1c4f3 100644 --- a/src/Magnum/GL/Test/ContextTest.cpp +++ b/src/Magnum/GL/Test/ContextTest.cpp @@ -101,7 +101,7 @@ void ContextTest::isExtension() { } /* Variadic check (used in variadic Configuration::addDisabledExtensions()), - check that it properly fails for each occurence of a non-extension */ + check that it properly fails for each occurrence of a non-extension */ #ifndef MAGNUM_TARGET_WEBGL CORRADE_VERIFY(Implementation::IsExtension< Extensions::KHR::debug, diff --git a/src/Magnum/Math/DualComplex.h b/src/Magnum/Math/DualComplex.h index 8e32b917e..e17daa110 100644 --- a/src/Magnum/Math/DualComplex.h +++ b/src/Magnum/Math/DualComplex.h @@ -108,7 +108,7 @@ template class DualComplex: public Dual> { } /** - * @brief Create dual complext from rotation complex and translation vector + * @brief Create dual complex from rotation complex and translation vector * @m_since_latest * * @f[ @@ -242,7 +242,7 @@ template class DualComplex: public Dual> { } /** - * @brief Multipy with dual complex number + * @brief Multiply with dual complex number * * @f[ * \hat a \hat b = a_0 b_0 + \epsilon (a_0 b_\epsilon + a_\epsilon) diff --git a/src/Magnum/MeshTools/RemoveDuplicates.cpp b/src/Magnum/MeshTools/RemoveDuplicates.cpp index fdb287db3..9e5fe1d84 100644 --- a/src/Magnum/MeshTools/RemoveDuplicates.cpp +++ b/src/Magnum/MeshTools/RemoveDuplicates.cpp @@ -73,7 +73,7 @@ std::size_t removeDuplicatesInto(const Containers::StridedArrayView2D table{ @@ -113,7 +113,7 @@ std::size_t removeDuplicatesInPlaceInto(const Containers::StridedArrayView2D table{ diff --git a/src/Magnum/MeshTools/Tipsify.cpp b/src/Magnum/MeshTools/Tipsify.cpp index d27e87194..556de89a6 100644 --- a/src/Magnum/MeshTools/Tipsify.cpp +++ b/src/Magnum/MeshTools/Tipsify.cpp @@ -39,7 +39,7 @@ template void tipsifyInPlaceImplementation(const Containers::StridedArr Containers::Array liveTriangleCount, neighborOffset, neighbors; Implementation::buildAdjacency(indices, vertexCount, liveTriangleCount, neighborOffset, neighbors); - /* Global time, per-vertex caching timestamps, per-triangle emmited flag */ + /* Global time, per-vertex caching timestamps, per-triangle emitted flag */ UnsignedInt time = cacheSize+1; Containers::Array timestamp{vertexCount}; /** @todo Have some bitset/staticbitset class for this */ diff --git a/src/Magnum/Platform/AndroidApplication.h b/src/Magnum/Platform/AndroidApplication.h index 7790a0662..4157b2ea4 100644 --- a/src/Magnum/Platform/AndroidApplication.h +++ b/src/Magnum/Platform/AndroidApplication.h @@ -758,7 +758,7 @@ class AndroidApplication::MouseEvent: public InputEvent { /** * Left mouse button. Note that this button is not set if only - * touch or stylus event occured. + * touch or stylus event occurred. * @attention Available since Android 4.0 (API level 14), not * detectable in earlier versions. */ @@ -827,7 +827,7 @@ class AndroidApplication::MouseMoveEvent: public InputEvent { enum class Button: std::int32_t { /** * Left mouse button. Note that this button is not set if only - * touch or stylus event occured. + * touch or stylus event occurred. * @attention Available since Android 4.0 (API level 14), not * detectable in earlier versions. */ diff --git a/src/Magnum/Platform/EmscriptenApplication.cpp b/src/Magnum/Platform/EmscriptenApplication.cpp index 5e3e018ce..500829dc8 100644 --- a/src/Magnum/Platform/EmscriptenApplication.cpp +++ b/src/Magnum/Platform/EmscriptenApplication.cpp @@ -284,7 +284,7 @@ void EmscriptenApplication::create(const Configuration& configuration, const GLC Vector2 EmscriptenApplication::dpiScaling(const Configuration& configuration) const { std::ostream* verbose = _verboseLog ? Debug::output() : nullptr; - /* Use values from the configuration only if not overriden on command line. + /* Use values from the configuration only if not overridden on command line. In any case explicit scaling has a precedence before the policy. */ if(!_commandLineDpiScaling.isZero()) { Debug{verbose} << "Platform::EmscriptenApplication: user-defined DPI scaling" << _commandLineDpiScaling; @@ -824,7 +824,7 @@ void EmscriptenApplication::redraw() { is possible), and no amount of \\\\$$$ helps avoiding that xylophone); but doing so means we forever hardcode what functions are exported and thus whatever extra Emscripten needs - to export will be overriden by this, causing only pain and + to export will be overridden by this, causing only pain and misery. So instead we rely on the implementation details of dynCall, diff --git a/src/Magnum/Platform/EmscriptenApplication.h b/src/Magnum/Platform/EmscriptenApplication.h index 949355f56..aa95ec83a 100644 --- a/src/Magnum/Platform/EmscriptenApplication.h +++ b/src/Magnum/Platform/EmscriptenApplication.h @@ -234,7 +234,7 @@ for a guide covering all platform differences. For this application in particular, @ref windowSize() can be different than @ref framebufferSize() on HiDPI displays --- which is different from @ref Sdl2Application behavior on Emscripten. By default, @ref dpiScaling() is -@cpp 1.0f @ce in both dimensions but it can be overriden using custom DPI +@cpp 1.0f @ce in both dimensions but it can be overridden using custom DPI scaling --- the `--magnum-dpi-scaling` command-line options are supported the same way as in @ref Sdl2Application, only in the form of URL GET parameters, similarly to all other @ref platforms-html5-environment "command-line options". diff --git a/src/Magnum/Platform/GlfwApplication.cpp b/src/Magnum/Platform/GlfwApplication.cpp index 70da60d79..0f0d4574c 100644 --- a/src/Magnum/Platform/GlfwApplication.cpp +++ b/src/Magnum/Platform/GlfwApplication.cpp @@ -147,7 +147,7 @@ Vector2 GlfwApplication::dpiScaling(const Configuration& configuration) { /** @todo */ #endif - /* Use values from the configuration only if not overriden on command line + /* Use values from the configuration only if not overridden on command line to something non-default. In any case explicit scaling has a precedence before the policy. */ Implementation::GlfwDpiScalingPolicy dpiScalingPolicy{}; diff --git a/src/Magnum/Platform/GlfwApplication.h b/src/Magnum/Platform/GlfwApplication.h index 07d7c9a93..e5941d99d 100644 --- a/src/Magnum/Platform/GlfwApplication.h +++ b/src/Magnum/Platform/GlfwApplication.h @@ -1124,7 +1124,7 @@ class GlfwApplication::Configuration { * @brief DPI scaling policy * * DPI scaling policy when requesting a particular window size. Can - * be overriden on command-line using `--magnum-dpi-scaling` or via + * be overridden on command-line using `--magnum-dpi-scaling` or via * the `MAGNUM_DPI_SCALING` environment variable. * @see @ref setSize(), @ref Platform-Sdl2Application-dpi */ diff --git a/src/Magnum/Platform/Sdl2Application.cpp b/src/Magnum/Platform/Sdl2Application.cpp index 8a536e9e7..4effb5ee8 100644 --- a/src/Magnum/Platform/Sdl2Application.cpp +++ b/src/Magnum/Platform/Sdl2Application.cpp @@ -207,7 +207,7 @@ Vector2 Sdl2Application::dpiScaling(const Configuration& configuration) { /* Handled below, warning printed only when using virtual DPI scaling */ #endif - /* Use values from the configuration only if not overriden on command line + /* Use values from the configuration only if not overridden on command line to something non-default. In any case explicit scaling has a precedence before the policy. */ Implementation::Sdl2DpiScalingPolicy dpiScalingPolicy{}; @@ -887,7 +887,7 @@ bool Sdl2Application::mainLoopIteration() { switch(event.type) { case SDL_WINDOWEVENT: switch(event.window.event) { - /* Not using SDL_WINDOWEVENT_RESIZED, because that doens't + /* Not using SDL_WINDOWEVENT_RESIZED, because that doesn't get fired when the window is resized programmatically (such as through setMaxWindowSize()) */ case SDL_WINDOWEVENT_SIZE_CHANGED: { diff --git a/src/Magnum/Platform/Sdl2Application.h b/src/Magnum/Platform/Sdl2Application.h index f0561bbdd..b4fdbe514 100644 --- a/src/Magnum/Platform/Sdl2Application.h +++ b/src/Magnum/Platform/Sdl2Application.h @@ -419,25 +419,25 @@ The default is depending on the platform: @ref windowSize() and @ref framebufferSize() will differ depending on whether `NSHighResolutionCapable` is enabled in the `*.plist` file or not. By default, @ref dpiScaling() is @cpp 1.0f @ce in both dimensions but it - can be overriden using custom DPI scaling. + can be overridden using custom DPI scaling. - On Windows, the default is @ref Configuration::DpiScalingPolicy::Framebuffer. The @ref windowSize() and @ref framebufferSize() is always the same. Depending on whether the DPI awareness was enabled in the manifest file or set by the `SetProcessDpiAwareness()` API, @ref dpiScaling() is either @cpp 1.0f @ce in both dimensions, indicating a low-DPI screen or a non-DPI-aware app, or some other value for HiDPI screens. In both cases the - value can be overriden using custom DPI scaling. + value can be overridden using custom DPI scaling. - On Linux, the default is @ref Configuration::DpiScalingPolicy::Virtual, taken from the `Xft.dpi` property. If the property is not available, it falls back to @ref Configuration::DpiScalingPolicy::Physical, querying the monitor DPI value. The @ref windowSize() and @ref framebufferSize() is always the same, @ref dpiScaling() contains the queried DPI scaling value. - The value can be overriden using custom DPI scaling. + The value can be overridden using custom DPI scaling. - On @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten", the default is physical DPI scaling, taken from [Window.getDevicePixelRatio()](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio). The @ref windowSize() and @ref framebufferSize() is always the same, @ref dpiScaling() contains the queried DPI scaling value. The value can be - overriden using custom DPI scaling. Note that this is different from the + overridden using custom DPI scaling. Note that this is different from the behavior in @ref EmscriptenApplication --- Emscripten's SDL implementation has some additional emulation code that reports event coordinates in framebuffer pixels instead of CSS pixels. See @@ -1755,7 +1755,7 @@ class Sdl2Application::Configuration { * @brief DPI scaling policy * * DPI scaling policy when requesting a particular window size. Can - * be overriden on command-line using `--magnum-dpi-scaling` or via + * be overridden on command-line using `--magnum-dpi-scaling` or via * the `MAGNUM_DPI_SCALING` environment variable. * @see @ref setSize(), @ref Platform-Sdl2Application-dpi */ diff --git a/src/Magnum/Primitives/Test/GradientTest.cpp b/src/Magnum/Primitives/Test/GradientTest.cpp index 1df2e159e..bb0f852e1 100644 --- a/src/Magnum/Primitives/Test/GradientTest.cpp +++ b/src/Magnum/Primitives/Test/GradientTest.cpp @@ -60,7 +60,7 @@ GradientTest::GradientTest() { using namespace Magnum::Math::Literals; void GradientTest::gradient2D() { - /* The corners sould have 0.2, 0.4, 0.6, 0.8 blends */ + /* The corners should have 0.2, 0.4, 0.6, 0.8 blends */ Trade::MeshData gradient = Primitives::gradient2D( {-1.0f, 2.0f}, {0.2f, 0.6f, 1.0f}, {1.0f, -2.0f}, {0.4f, 1.0f, 0.0f}); @@ -115,7 +115,7 @@ void GradientTest::gradient2DVertical() { } void GradientTest::gradient3D() { - /* The corners sould have 0.2, 0.4, 0.6, 0.8 blends */ + /* The corners should have 0.2, 0.4, 0.6, 0.8 blends */ Trade::MeshData gradient = Primitives::gradient3D( {-1.0f, 2.0f, -1.5f}, {0.2f, 0.6f, 1.0f}, {1.0f, -2.0f, -1.5f}, {0.4f, 1.0f, 0.0f}); diff --git a/src/Magnum/SceneGraph/Object.hpp b/src/Magnum/SceneGraph/Object.hpp index 4e44e795d..6267c5558 100644 --- a/src/Magnum/SceneGraph/Object.hpp +++ b/src/Magnum/SceneGraph/Object.hpp @@ -211,7 +211,7 @@ template std::vector Ob /* Mark all original objects as joints and create initial list of joints from them */ for(std::size_t i = 0; i != objects.size(); ++i) { - /* Multiple occurences of one object in the array, don't overwrite it + /* Multiple occurrences of one object in the array, don't overwrite it with different counter */ if(objects[i].get().counter != 0xFFFFu) continue; @@ -231,7 +231,7 @@ template std::vector Ob /* Mark all objects up the hierarchy as visited */ auto it = objects.begin(); while(!objects.empty()) { - /* Already visited, remove and continue to next (duplicate occurence) */ + /* Already visited, remove and continue to next (duplicate occurrence) */ if(it->get().flags & Flag::Visited) { it = objects.erase(it); continue; @@ -276,7 +276,7 @@ template std::vector Ob for(std::size_t i = 0; i != jointTransformations.size(); ++i) computeJointTransformation(jointObjects, jointTransformations, i, finalTransformation); - /* Copy transformation for second or next occurences from first occurence + /* Copy transformation for second or next occurrences from first occurrence of duplicate object */ for(std::size_t i = 0; i != objectCount; ++i) { if(jointObjects[i].get().counter != i) @@ -285,7 +285,7 @@ template std::vector Ob /* All visited marks are now cleaned, clean joint marks and counters */ for(auto i: jointObjects) { - /* All not-already cleaned objects (...duplicate occurences) should + /* All not-already cleaned objects (...duplicate occurrences) should have joint mark */ CORRADE_INTERNAL_ASSERT(i.get().counter == 0xFFFFu || i.get().flags & Flag::Joint); i.get().flags &= ~Flag::Joint; @@ -301,7 +301,7 @@ template typename Transformation::DataType Object> o = jointObjects[joint]; /* Transformation already computed ("unvisited" by this function before - either due to recursion or duplicate object occurences), done */ + either due to recursion or duplicate object occurrences), done */ if(!(o.get().flags & Flag::Visited)) return jointTransformations[joint]; /* Initialize transformation */ diff --git a/src/Magnum/SceneGraph/Test/AnimableTest.cpp b/src/Magnum/SceneGraph/Test/AnimableTest.cpp index 36306c1c4..e2bff1025 100644 --- a/src/Magnum/SceneGraph/Test/AnimableTest.cpp +++ b/src/Magnum/SceneGraph/Test/AnimableTest.cpp @@ -370,7 +370,7 @@ template void AnimableTest::pause() { CORRADE_COMPARE(animable.time, 1.5f); /* Unpausing, next step should continue from absolute time when pause - occured */ + occurred */ animable.setState(AnimationState::Running); group.step(5.0f, 0.5f); CORRADE_COMPARE(animable.state(), AnimationState::Running); diff --git a/src/Magnum/Shaders/FlatGL.h b/src/Magnum/Shaders/FlatGL.h index ffca52dac..d0bcc08fb 100644 --- a/src/Magnum/Shaders/FlatGL.h +++ b/src/Magnum/Shaders/FlatGL.h @@ -85,7 +85,7 @@ Common rendering setup: If you want to use a texture, you need to provide also the @ref TextureCoordinates attribute. Pass @ref Flag::Textured to the constructor and then at render time don't forget to bind also the texture via -@ref bindTexture(). The texture is multipled by the color, which is by default +@ref bindTexture(). The texture is multiplied by the color, which is by default set to @cpp 0xffffffff_rgbaf @ce. Common mesh setup: @snippet MagnumShaders-gl.cpp FlatGL-usage-textured1 diff --git a/src/Magnum/Shaders/MeshVisualizerGL.h b/src/Magnum/Shaders/MeshVisualizerGL.h index 878418692..ca7197f55 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.h +++ b/src/Magnum/Shaders/MeshVisualizerGL.h @@ -669,7 +669,7 @@ this, the mesh will be rendered in a single color. If you don't have geometry shaders, you need to enable @ref Flag::NoGeometryShader (done by default in OpenGL ES 2.0) and use only **non-indexed** triangle meshes -(see @ref MeshTools::duplicate() for a possible solution). Additionaly, if you +(see @ref MeshTools::duplicate() for a possible solution). Additionally, if you have OpenGL < 3.1 or OpenGL ES 2.0, you need to provide also the @ref VertexIndex attribute. diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index 5c1f014fe..37a06aa0f 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -66,7 +66,7 @@ If you want to use textures, you need to provide also the the constructor and then at render time don't forget to also call appropriate subset of @ref bindAmbientTexture(), @ref bindDiffuseTexture() and @ref bindSpecularTexture() (or the combined @ref bindTextures()). The texture -is multipled by the color, which is by default set to fully opaque white for +is multiplied by the color, which is by default set to fully opaque white for enabled textures. Mesh setup with a diffuse and a specular texture: @snippet MagnumShaders-gl.cpp PhongGL-usage-texture1 diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 4a1c18461..c70e5d332 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -655,7 +655,7 @@ const struct { {"point, range=0.0", "light-none.tga", {0.75f, -0.75f, -0.75f, 1.0f}, Color3{1.0f}, Color3{1.0f}, 1.0f, 0.0f, {}}, - /* Distance is 0, which means the direction is always prependicular and + /* Distance is 0, which means the direction is always perpendicular and thus contributes nothing */ {"point, distance=0", "light-none.tga", {0.75f, -0.75f, -0.75f, 1.0f}, Color3{1.0f}, Color3{1.0f}, diff --git a/src/Magnum/Text/Renderer.cpp b/src/Magnum/Text/Renderer.cpp index e6dfd0762..c0b059f9c 100644 --- a/src/Magnum/Text/Renderer.cpp +++ b/src/Magnum/Text/Renderer.cpp @@ -72,7 +72,7 @@ std::tuple, Range2D> renderVerticesInternal(AbstractFont& fo std::vector vertices; vertices.reserve(text.size()*4); - /* Total rendered bounds, intial line position, line increment, last+1 + /* Total rendered bounds, initial line position, line increment, last+1 vertex on previous line */ Range2D rectangle; Vector2 linePosition; diff --git a/src/Magnum/Trade/AbstractImporter.h b/src/Magnum/Trade/AbstractImporter.h index 720c40b3e..310143e79 100644 --- a/src/Magnum/Trade/AbstractImporter.h +++ b/src/Magnum/Trade/AbstractImporter.h @@ -283,7 +283,7 @@ properly use the callbacks to both load the top-level file in @ref doOpenFile() and also load any external files when needed. The @ref doOpenFile() can delegate back into the base implementation, but it should remember at least the base file path to pass correct paths to subsequent file callbacks. The -@ref doSetFileCallback() can be overriden in case it's desired to respond to +@ref doSetFileCallback() can be overridden in case it's desired to respond to file loading callback setup, but doesn't have to be. For multi-data formats the file opening shouldn't take long and all parsing @@ -1526,7 +1526,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Default implementation returns @cpp 0 @ce. This function isn't * expected to fail --- if an import error occus, it should be handled - * preferrably during @ref doScene() (with correct scene count + * preferably during @ref doScene() (with correct scene count * reported), and if not possible, already during file opening. */ virtual UnsignedInt doSceneCount() const; @@ -1553,7 +1553,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Default implementation returns @cpp 0 @ce. This function isn't * expected to fail --- if an import error occus, it should be handled - * preferrably during @ref doAnimation() (with correct animation count + * preferably during @ref doAnimation() (with correct animation count * reported), and if not possible, already during file opening. */ virtual UnsignedInt doAnimationCount() const; @@ -1580,7 +1580,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Default implementation returns @cpp 0 @ce. This function isn't * expected to fail --- if an import error occus, it should be handled - * preferrably during @ref doLight() (with correct light count + * preferably during @ref doLight() (with correct light count * reported), and if not possible, already during file opening. */ virtual UnsignedInt doLightCount() const; @@ -1607,7 +1607,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Default implementation returns @cpp 0 @ce. This function isn't * expected to fail --- if an import error occus, it should be handled - * preferrably during @ref doCamera() (with correct camera count + * preferably during @ref doCamera() (with correct camera count * reported), and if not possible, already during file opening. */ virtual UnsignedInt doCameraCount() const; @@ -1634,7 +1634,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Default implementation returns @cpp 0 @ce. This function isn't * expected to fail --- if an import error occus, it should be handled - * preferrably during @ref doObject2D() (with correct object count + * preferably during @ref doObject2D() (with correct object count * reported), and if not possible, already during file opening. */ virtual UnsignedInt doObject2DCount() const; @@ -1661,7 +1661,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Default implementation returns @cpp 0 @ce. This function isn't * expected to fail --- if an import error occus, it should be handled - * preferrably during @ref doObject3D() (with correct object count + * preferably during @ref doObject3D() (with correct object count * reported), and if not possible, already during file opening. */ virtual UnsignedInt doObject3DCount() const; @@ -1689,7 +1689,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Default implementation returns @cpp 0 @ce. This function isn't * expected to fail --- if an import error occus, it should be handled - * preferrably during @ref doSkin2D() (with correct skin count + * preferably during @ref doSkin2D() (with correct skin count * reported), and if not possible, already during file opening. */ virtual UnsignedInt doSkin2DCount() const; @@ -1722,7 +1722,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Default implementation returns @cpp 0 @ce. This function isn't * expected to fail --- if an import error occus, it should be handled - * preferrably during @ref doSkin3D() (with correct skin count + * preferably during @ref doSkin3D() (with correct skin count * reported), and if not possible, already during file opening. */ virtual UnsignedInt doSkin3DCount() const; @@ -1755,7 +1755,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Default implementation returns @cpp 0 @ce. This function isn't * expected to fail --- if an import error occus, it should be handled - * preferrably during @ref doMesh() (with correct mesh count + * preferably during @ref doMesh() (with correct mesh count * reported), and if not possible, already during file opening. */ virtual UnsignedInt doMeshCount() const; @@ -1931,7 +1931,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Default implementation returns @cpp 0 @ce. This function isn't * expected to fail --- if an import error occus, it should be handled - * preferrably during @ref doMaterial() (with correct material count + * preferably during @ref doMaterial() (with correct material count * reported), and if not possible, already during file opening. */ virtual UnsignedInt doMaterialCount() const; @@ -1958,7 +1958,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Default implementation returns @cpp 0 @ce. This function isn't * expected to fail --- if an import error occus, it should be handled - * preferrably during @ref doTexture() (with correct texture count + * preferably during @ref doTexture() (with correct texture count * reported), and if not possible, already during file opening. */ virtual UnsignedInt doTextureCount() const; @@ -1985,7 +1985,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Default implementation returns @cpp 0 @ce. This function isn't * expected to fail --- if an import error occus, it should be handled - * preferrably during @ref doImage1D() (with correct image count + * preferably during @ref doImage1D() (with correct image count * reported), and if not possible, already during file opening. */ virtual UnsignedInt doImage1DCount() const; @@ -2021,7 +2021,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Default implementation returns @cpp 0 @ce. This function isn't * expected to fail --- if an import error occus, it should be handled - * preferrably during @ref doImage2D() (with correct image count + * preferably during @ref doImage2D() (with correct image count * reported), and if not possible, already during file opening. */ virtual UnsignedInt doImage2DCount() const; @@ -2065,7 +2065,7 @@ class MAGNUM_TRADE_EXPORT AbstractImporter: public PluginManager::AbstractManagi * * Default implementation returns @cpp 0 @ce. This function isn't * expected to fail --- if an import error occus, it should be handled - * preferrably during @ref doImage3D() (with correct image count + * preferably during @ref doImage3D() (with correct image count * reported), and if not possible, already during file opening. */ virtual UnsignedInt doImage3DCount() const; diff --git a/src/Magnum/Trade/MeshData.h b/src/Magnum/Trade/MeshData.h index a83083fb9..6b84034ae 100644 --- a/src/Magnum/Trade/MeshData.h +++ b/src/Magnum/Trade/MeshData.h @@ -447,7 +447,7 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData { * used in most @ref MeshTools algorithms. * * Additionally, for even more flexibility, the @p vertexCount can be - * overriden at @ref MeshData construction time, however all attributes + * overridden at @ref MeshData construction time, however all attributes * are still required to have the same vertex count to catch accidents. * * Note that due to the @cpp constexpr @ce nature of this constructor, diff --git a/src/Magnum/Trade/PbrClearCoatMaterialData.h b/src/Magnum/Trade/PbrClearCoatMaterialData.h index b8e2039f5..77f71a168 100644 --- a/src/Magnum/Trade/PbrClearCoatMaterialData.h +++ b/src/Magnum/Trade/PbrClearCoatMaterialData.h @@ -72,7 +72,7 @@ class MAGNUM_TRADE_EXPORT PbrClearCoatMaterialData: public MaterialLayerData::value); /* Variadic check (used in variadic addEnabledExtensions()), check that it - properly fails for each occurence of a device extension */ + properly fails for each occurrence of a device extension */ CORRADE_VERIFY(Implementation::IsInstanceExtension< Extensions::KHR::get_physical_device_properties2, Extensions::KHR::external_memory_capabilities, @@ -103,7 +103,7 @@ void ExtensionsTest::isExtension() { } /* Variadic check (used in variadic addEnabledExtensions()), check that it - properly fails for each occurence of a device extension */ + properly fails for each occurrence of a device extension */ CORRADE_VERIFY(Implementation::IsExtension< Extensions::KHR::external_memory, Extensions::KHR::depth_stencil_resolve, diff --git a/src/Magnum/Vk/Test/MeshVkTest.cpp b/src/Magnum/Vk/Test/MeshVkTest.cpp index 529cb6abd..8bde055c1 100644 --- a/src/Magnum/Vk/Test/MeshVkTest.cpp +++ b/src/Magnum/Vk/Test/MeshVkTest.cpp @@ -319,7 +319,7 @@ void MeshVkTest::cmdDrawIndexed() { { Buffer buffer{device(), BufferCreateInfo{ BufferUsage::VertexBuffer|BufferUsage::IndexBuffer, - /* Artifical offset at the beginning to test that the offset is + /* Artificial offset at the beginning to test that the offset is used correctly in both cases */ 32 + 12*4 + sizeof(QuadIndexData) }, MemoryFlag::HostVisible}; From fb2eddb6bbfca5aa095b2cac70c9392da962f792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 22 Jun 2021 16:06:00 +0200 Subject: [PATCH 79/93] doc: updated credits and changelog. --- doc/changelog.dox | 5 +++-- doc/credits.dox | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 97eebaf71..4244c5844 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -710,8 +710,9 @@ See also: - Added a note about MinGW GCC and Clang ABI incompatibility to @ref platforms-windows "Windows platform docs" (see [mosra/magnum#227](https://github.com/mosra/magnum/issues/227) and [mosra/magnum#439](https://github.com/mosra/magnum/issues/439)) -- Various documentation fixes (see [mosra/magnum#492](https://github.com/mosra/magnum/issues/492)) -- @ref Corrade::Utility::Debug and friends were always brought to the ) +- Various documentation fixes (see [mosra/magnum#492](https://github.com/mosra/magnum/issues/492), + [mosra/magnum#521](https://github.com/mosra/magnum/pull/521)) +- @ref Corrade::Utility::Debug and friends were always brought to the @ref Magnum namespace via a @cpp using @ce directive, but this was never reflected in the docs and thus hidden from users. Now it's shown in the docs as a set of @ref Debug etc typedefs instead, to make them more diff --git a/doc/credits.dox b/doc/credits.dox index 257920337..2001cd2fc 100644 --- a/doc/credits.dox +++ b/doc/credits.dox @@ -82,7 +82,7 @@ Are the below lists missing your name or something's wrong? [Let us know!](https://magnum.graphics/contact/) - **Aaron Gokaslan** ([\@Skylion007](https://github.com/Skylion007)) --- - various minor code modernization + various minor code modernization, typo fixes - **[\@abgita](https://github.com/abgita)** --- minor typo fixes - **Alan Jefferson** ([\@alanjfs](https://github.com/alanjfs)) --- extensive usability and first-time-use feedback From 22d14848146778456b660086004143098d970e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 22 Jun 2021 16:22:15 +0200 Subject: [PATCH 80/93] Test: rename Any* plugin test cases for clarity. --- .../AnyImageConverter/Test/AnyImageConverterTest.cpp | 6 +++--- .../AnyImageImporter/Test/AnyImageImporterTest.cpp | 6 +++--- .../AnySceneConverter/Test/AnySceneConverterTest.cpp | 12 ++++++------ .../AnySceneImporter/Test/AnySceneImporterTest.cpp | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/MagnumPlugins/AnyImageConverter/Test/AnyImageConverterTest.cpp b/src/MagnumPlugins/AnyImageConverter/Test/AnyImageConverterTest.cpp index 6bf445a5c..c449fec11 100644 --- a/src/MagnumPlugins/AnyImageConverter/Test/AnyImageConverterTest.cpp +++ b/src/MagnumPlugins/AnyImageConverter/Test/AnyImageConverterTest.cpp @@ -48,7 +48,7 @@ struct AnyImageConverterTest: TestSuite::Tester { void unknown(); - void verbose(); + void propagateFlags(); /* Explicitly forbid system-wide plugin dependencies */ PluginManager::Manager _manager{"nonexistent"}; @@ -84,7 +84,7 @@ AnyImageConverterTest::AnyImageConverterTest() { addTests({&AnyImageConverterTest::unknown, - &AnyImageConverterTest::verbose}); + &AnyImageConverterTest::propagateFlags}); /* Load the plugin directly from the build tree. Otherwise it's static and already loaded. */ @@ -155,7 +155,7 @@ void AnyImageConverterTest::unknown() { CORRADE_COMPARE(output.str(), "Trade::AnyImageConverter::convertToFile(): cannot determine the format of image.xcf\n"); } -void AnyImageConverterTest::verbose() { +void AnyImageConverterTest::propagateFlags() { if(!(_manager.loadState("TgaImageConverter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("TgaImageConverter plugin not enabled, cannot test"); diff --git a/src/MagnumPlugins/AnyImageImporter/Test/AnyImageImporterTest.cpp b/src/MagnumPlugins/AnyImageImporter/Test/AnyImageImporterTest.cpp index d1ad081fc..8179fdde4 100644 --- a/src/MagnumPlugins/AnyImageImporter/Test/AnyImageImporterTest.cpp +++ b/src/MagnumPlugins/AnyImageImporter/Test/AnyImageImporterTest.cpp @@ -49,7 +49,7 @@ struct AnyImageImporterTest: TestSuite::Tester { void unknownSignature(); void emptyData(); - void verbose(); + void propagateFlags(); /* Explicitly forbid system-wide plugin dependencies */ PluginManager::Manager _manager{"nonexistent"}; @@ -130,7 +130,7 @@ AnyImageImporterTest::AnyImageImporterTest() { addTests({&AnyImageImporterTest::emptyData}); - addInstancedTests({&AnyImageImporterTest::verbose}, + addInstancedTests({&AnyImageImporterTest::propagateFlags}, Containers::arraySize(LoadData)); /* Load the plugin directly from the build tree. Otherwise it's static and @@ -222,7 +222,7 @@ void AnyImageImporterTest::emptyData() { CORRADE_COMPARE(output.str(), "Trade::AnyImageImporter::openData(): file is empty\n"); } -void AnyImageImporterTest::verbose() { +void AnyImageImporterTest::propagateFlags() { auto&& data = LoadData[testCaseInstanceId()]; setTestCaseDescription(data.name); diff --git a/src/MagnumPlugins/AnySceneConverter/Test/AnySceneConverterTest.cpp b/src/MagnumPlugins/AnySceneConverter/Test/AnySceneConverterTest.cpp index e545d5dbf..5653a5be7 100644 --- a/src/MagnumPlugins/AnySceneConverter/Test/AnySceneConverterTest.cpp +++ b/src/MagnumPlugins/AnySceneConverter/Test/AnySceneConverterTest.cpp @@ -41,12 +41,12 @@ namespace Magnum { namespace Trade { namespace Test { namespace { struct AnySceneConverterTest: TestSuite::Tester { explicit AnySceneConverterTest(); - void load(); + void convert(); void detect(); void unknown(); - void verbose(); + void propagateFlags(); /* Explicitly forbid system-wide plugin dependencies */ PluginManager::Manager _manager{"nonexistent"}; @@ -62,14 +62,14 @@ constexpr struct { }; AnySceneConverterTest::AnySceneConverterTest() { - addTests({&AnySceneConverterTest::load}); + addTests({&AnySceneConverterTest::convert}); addInstancedTests({&AnySceneConverterTest::detect}, Containers::arraySize(DetectData)); addTests({&AnySceneConverterTest::unknown, - &AnySceneConverterTest::verbose}); + &AnySceneConverterTest::propagateFlags}); /* Load the plugin directly from the build tree. Otherwise it's static and already loaded. */ @@ -81,7 +81,7 @@ AnySceneConverterTest::AnySceneConverterTest() { CORRADE_INTERNAL_ASSERT_OUTPUT(Utility::Directory::mkpath(ANYSCENECONVERTER_TEST_OUTPUT_DIR)); } -void AnySceneConverterTest::load() { +void AnySceneConverterTest::convert() { CORRADE_SKIP("No scene converter plugin available to test."); } @@ -114,7 +114,7 @@ void AnySceneConverterTest::unknown() { CORRADE_COMPARE(output.str(), "Trade::AnySceneConverter::convertToFile(): cannot determine the format of mesh.obj\n"); } -void AnySceneConverterTest::verbose() { +void AnySceneConverterTest::propagateFlags() { CORRADE_SKIP("No plugin available to test."); } diff --git a/src/MagnumPlugins/AnySceneImporter/Test/AnySceneImporterTest.cpp b/src/MagnumPlugins/AnySceneImporter/Test/AnySceneImporterTest.cpp index 9f5f88610..0aef4c5c9 100644 --- a/src/MagnumPlugins/AnySceneImporter/Test/AnySceneImporterTest.cpp +++ b/src/MagnumPlugins/AnySceneImporter/Test/AnySceneImporterTest.cpp @@ -56,7 +56,7 @@ struct AnySceneImporterTest: TestSuite::Tester { void unknown(); - void verbose(); + void propagateFlags(); /* Explicitly forbid system-wide plugin dependencies */ PluginManager::Manager _manager{"nonexistent"}; @@ -98,7 +98,7 @@ AnySceneImporterTest::AnySceneImporterTest() { addTests({&AnySceneImporterTest::unknown, - &AnySceneImporterTest::verbose}); + &AnySceneImporterTest::propagateFlags}); /* Load the plugin directly from the build tree. Otherwise it's static and already loaded. */ @@ -181,7 +181,7 @@ void AnySceneImporterTest::unknown() { CORRADE_COMPARE(output.str(), "Trade::AnySceneImporter::openFile(): cannot determine the format of mesh.wtf\n"); } -void AnySceneImporterTest::verbose() { +void AnySceneImporterTest::propagateFlags() { if(!(_manager.loadState("ObjImporter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("ObjImporter plugin not enabled, cannot test"); From d6a3be571b0173a0939e82d2e27894c81118c483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 22 Jun 2021 18:14:26 +0200 Subject: [PATCH 81/93] AnySceneConverter,AnySceneImporter: expand tests with external plugins. The AnyShaderConverter tests are doing this already anyway, so why not here as well. --- .../Test/AnySceneConverterTest.cpp | 66 +++++++++++++++++- .../AnySceneConverter/Test/CMakeLists.txt | 6 +- .../AnySceneConverter/Test/configure.h.cmake | 15 ++++ .../Test/AnySceneImporterTest.cpp | 25 ++++--- .../AnySceneImporter/Test/CMakeLists.txt | 5 +- .../AnySceneImporter/Test/configure.h.cmake | 15 ++++ .../AnySceneImporter/Test/triangle.ply | Bin 0 -> 219 bytes 7 files changed, 119 insertions(+), 13 deletions(-) create mode 100644 src/MagnumPlugins/AnySceneImporter/Test/triangle.ply diff --git a/src/MagnumPlugins/AnySceneConverter/Test/AnySceneConverterTest.cpp b/src/MagnumPlugins/AnySceneConverter/Test/AnySceneConverterTest.cpp index 5653a5be7..73c52eae1 100644 --- a/src/MagnumPlugins/AnySceneConverter/Test/AnySceneConverterTest.cpp +++ b/src/MagnumPlugins/AnySceneConverter/Test/AnySceneConverterTest.cpp @@ -25,12 +25,15 @@ #include #include +#include #include #include +#include #include #include #include +#include "Magnum/Math/Vector3.h" #include "Magnum/Trade/AbstractSceneConverter.h" #include "Magnum/Trade/MeshData.h" @@ -82,7 +85,33 @@ AnySceneConverterTest::AnySceneConverterTest() { } void AnySceneConverterTest::convert() { - CORRADE_SKIP("No scene converter plugin available to test."); + PluginManager::Manager manager{MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR}; + #ifdef ANYSCENECONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSCENECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.loadState("StanfordSceneConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("StanfordSceneConverter plugin can't be loaded."); + + const std::string filename = Utility::Directory::join(ANYSCENECONVERTER_TEST_OUTPUT_DIR, "file.ply"); + + if(Utility::Directory::exists(filename)) + CORRADE_VERIFY(Utility::Directory::rm(filename)); + + const Vector3 positions[] { + {-0.5f, -0.5f, 0.0f}, + { 0.5f, -0.5f, 0.0f}, + { 0.0f, 0.5f, 0.0f} + }; + const Trade::MeshData mesh{MeshPrimitive::Triangles, {}, positions, { + Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::arrayView(positions)} + }}; + + Containers::Pointer converter = manager.instantiate("AnySceneConverter"); + CORRADE_VERIFY(converter->convertToFile(mesh, filename)); + /* This file is reused in AnySceneImporter tests, so it's worth to save it + here */ + CORRADE_COMPARE_AS(filename, PLY_FILE, TestSuite::Compare::File); } void AnySceneConverterTest::detect() { @@ -115,7 +144,40 @@ void AnySceneConverterTest::unknown() { } void AnySceneConverterTest::propagateFlags() { - CORRADE_SKIP("No plugin available to test."); + PluginManager::Manager manager{MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR}; + #ifdef ANYSCENECONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSCENECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.loadState("StanfordSceneConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("StanfordSceneConverter plugin can't be loaded."); + + const std::string filename = Utility::Directory::join(ANYSCENECONVERTER_TEST_OUTPUT_DIR, "file.ply"); + + const Vector3 positions[] { + {-0.5f, -0.5f, 0.0f}, + { 0.5f, -0.5f, 0.0f}, + { 0.0f, 0.5f, 0.0f} + }; + const Trade::MeshData mesh{MeshPrimitive::Triangles, {}, positions, { + Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::arrayView(positions)} + }}; + + Containers::Pointer converter = manager.instantiate("AnySceneConverter"); + converter->setFlags(SceneConverterFlag::Verbose); + + std::ostringstream out; + { + Debug redirectOutput{&out}; + CORRADE_VERIFY(converter->convertToFile(mesh, filename)); + CORRADE_VERIFY(Utility::Directory::exists(filename)); + } + CORRADE_COMPARE(out.str(), + "Trade::AnySceneConverter::convertToFile(): using StanfordSceneConverter\n"); + + /* We tested AnySceneConverter's verbose output, but can't actually test + the flag propagation in any way yet */ + CORRADE_SKIP("No plugin with verbose output available to test flag propagation."); } }}}} diff --git a/src/MagnumPlugins/AnySceneConverter/Test/CMakeLists.txt b/src/MagnumPlugins/AnySceneConverter/Test/CMakeLists.txt index 3b411839d..2672a88ca 100644 --- a/src/MagnumPlugins/AnySceneConverter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/AnySceneConverter/Test/CMakeLists.txt @@ -25,8 +25,10 @@ if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) set(ANYSCENECONVERTER_TEST_OUTPUT_DIR "write") + set(PLY_FILE triangle.ply) else() set(ANYSCENECONVERTER_TEST_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) + set(PLY_FILE ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/AnySceneImporter/Test/triangle.ply) endif() # CMake before 3.8 has broken $ expressions for iOS (see @@ -47,7 +49,9 @@ file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) corrade_add_test(AnySceneConverterTest AnySceneConverterTest.cpp - LIBRARIES MagnumTrade) + LIBRARIES MagnumTrade + FILES + ../../AnySceneImporter/Test/triangle.ply) target_include_directories(AnySceneConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) if(MAGNUM_ANYSCENECONVERTER_BUILD_STATIC) target_link_libraries(AnySceneConverterTest PRIVATE AnySceneConverter) diff --git a/src/MagnumPlugins/AnySceneConverter/Test/configure.h.cmake b/src/MagnumPlugins/AnySceneConverter/Test/configure.h.cmake index 89af33ff4..2d8e8e8de 100644 --- a/src/MagnumPlugins/AnySceneConverter/Test/configure.h.cmake +++ b/src/MagnumPlugins/AnySceneConverter/Test/configure.h.cmake @@ -25,3 +25,18 @@ #cmakedefine ANYSCENECONVERTER_PLUGIN_FILENAME "${ANYSCENECONVERTER_PLUGIN_FILENAME}" #define ANYSCENECONVERTER_TEST_OUTPUT_DIR "${ANYSCENECONVERTER_TEST_OUTPUT_DIR}" +#define PLY_FILE "${PLY_FILE}" + +#ifdef CORRADE_TARGET_WINDOWS +#ifdef CORRADE_IS_DEBUG_BUILD +#define MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_SCENECONVERTER_DEBUG_BINARY_INSTALL_DIR}" +#else +#define MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_SCENECONVERTER_RELEASE_BINARY_INSTALL_DIR}" +#endif +#else +#ifdef CORRADE_IS_DEBUG_BUILD +#define MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_SCENECONVERTER_DEBUG_LIBRARY_INSTALL_DIR}" +#else +#define MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_SCENECONVERTER_RELEASE_LIBRARY_INSTALL_DIR}" +#endif +#endif diff --git a/src/MagnumPlugins/AnySceneImporter/Test/AnySceneImporterTest.cpp b/src/MagnumPlugins/AnySceneImporter/Test/AnySceneImporterTest.cpp index 0aef4c5c9..62c1898aa 100644 --- a/src/MagnumPlugins/AnySceneImporter/Test/AnySceneImporterTest.cpp +++ b/src/MagnumPlugins/AnySceneImporter/Test/AnySceneImporterTest.cpp @@ -182,24 +182,31 @@ void AnySceneImporterTest::unknown() { } void AnySceneImporterTest::propagateFlags() { - if(!(_manager.loadState("ObjImporter") & PluginManager::LoadState::Loaded)) - CORRADE_SKIP("ObjImporter plugin not enabled, cannot test"); + PluginManager::Manager manager{MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR}; + #ifdef ANYSCENEIMPORTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSCENEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif - Containers::Pointer importer = _manager.instantiate("AnySceneImporter"); + if(manager.load("AssimpImporter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("AssimpImporter plugin can't be loaded."); + /* Ensure Assimp is used for PLY files and not our StanfordImporter */ + manager.setPreferredPlugins("StanfordImporter", {"AssimpImporter"}); + + Containers::Pointer importer = manager.instantiate("AnySceneImporter"); importer->setFlags(ImporterFlag::Verbose); std::ostringstream out; { Debug redirectOutput{&out}; - CORRADE_VERIFY(importer->openFile(OBJ_FILE)); + CORRADE_VERIFY(importer->openFile(PLY_FILE)); CORRADE_VERIFY(importer->mesh(0)); } - CORRADE_COMPARE(out.str(), - "Trade::AnySceneImporter::openFile(): using ObjImporter\n"); - /* We tested AnySceneImporter's verbose output, but can't actually test - the flag propagation in any way yet */ - CORRADE_SKIP("No plugin with verbose output available to test flag propagation."); + Containers::StringView expected = + "Trade::AnySceneImporter::openFile(): using StanfordImporter (provided by AssimpImporter)\n" + "Trade::AssimpImporter: Info, T0: Load " PLY_FILE "\n"; + /** @todo use Compare::StringPrefix(?) when it exists */ + CORRADE_COMPARE(out.str().substr(0, expected.size()), expected); } }}}} diff --git a/src/MagnumPlugins/AnySceneImporter/Test/CMakeLists.txt b/src/MagnumPlugins/AnySceneImporter/Test/CMakeLists.txt index fded7df55..e03f55098 100644 --- a/src/MagnumPlugins/AnySceneImporter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/AnySceneImporter/Test/CMakeLists.txt @@ -25,8 +25,10 @@ if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) set(OBJ_FILE pointMesh.obj) + set(PLY_FILE triangle.ply) else() set(OBJ_FILE ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/ObjImporter/Test/pointMesh.obj) + set(PLY_FILE ${CMAKE_CURRENT_SOURCE_DIR}/triangle.ply) endif() # CMake before 3.8 has broken $ expressions for iOS (see @@ -49,7 +51,8 @@ file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h corrade_add_test(AnySceneImporterTest AnySceneImporterTest.cpp LIBRARIES MagnumTrade FILES - ../../ObjImporter/Test/pointMesh.obj) + ../../ObjImporter/Test/pointMesh.obj + triangle.ply) target_include_directories(AnySceneImporterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) if(MAGNUM_ANYSCENEIMPORTER_BUILD_STATIC) target_link_libraries(AnySceneImporterTest PRIVATE AnySceneImporter) diff --git a/src/MagnumPlugins/AnySceneImporter/Test/configure.h.cmake b/src/MagnumPlugins/AnySceneImporter/Test/configure.h.cmake index 121acdf97..2b40b2f65 100644 --- a/src/MagnumPlugins/AnySceneImporter/Test/configure.h.cmake +++ b/src/MagnumPlugins/AnySceneImporter/Test/configure.h.cmake @@ -26,3 +26,18 @@ #cmakedefine ANYSCENEIMPORTER_PLUGIN_FILENAME "${ANYSCENEIMPORTER_PLUGIN_FILENAME}" #cmakedefine OBJIMPORTER_PLUGIN_FILENAME "${OBJIMPORTER_PLUGIN_FILENAME}" #define OBJ_FILE "${OBJ_FILE}" +#define PLY_FILE "${PLY_FILE}" + +#ifdef CORRADE_TARGET_WINDOWS +#ifdef CORRADE_IS_DEBUG_BUILD +#define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_DEBUG_BINARY_INSTALL_DIR}" +#else +#define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_RELEASE_BINARY_INSTALL_DIR}" +#endif +#else +#ifdef CORRADE_IS_DEBUG_BUILD +#define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_DEBUG_LIBRARY_INSTALL_DIR}" +#else +#define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_RELEASE_LIBRARY_INSTALL_DIR}" +#endif +#endif diff --git a/src/MagnumPlugins/AnySceneImporter/Test/triangle.ply b/src/MagnumPlugins/AnySceneImporter/Test/triangle.ply new file mode 100644 index 0000000000000000000000000000000000000000..36ce77768bd1f2d2f8225abf3e39e2e88050bfd9 GIT binary patch literal 219 zcmZ9GVG6=9427%y>nU;qad-faQfim7K-ZQmlXaK#YDUm0`|-$^7m}BnnvI1=FL0;q zxw%9l2_5F1e1`6Pv6yhgEbu@UT;OUem#W+#rlO0>-e`Mc-EKr55W3$y(Iha3!86RX hWjNCGfRoiP+ Date: Tue, 22 Jun 2021 18:30:02 +0200 Subject: [PATCH 82/93] AnyImageConverter: add placeholder tests for compressed 2D support. --- .../Test/AnyImageConverterTest.cpp | 50 ++++++++++++++----- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/src/MagnumPlugins/AnyImageConverter/Test/AnyImageConverterTest.cpp b/src/MagnumPlugins/AnyImageConverter/Test/AnyImageConverterTest.cpp index c449fec11..c35952eaa 100644 --- a/src/MagnumPlugins/AnyImageConverter/Test/AnyImageConverterTest.cpp +++ b/src/MagnumPlugins/AnyImageConverter/Test/AnyImageConverterTest.cpp @@ -43,12 +43,16 @@ namespace Magnum { namespace Trade { namespace Test { namespace { struct AnyImageConverterTest: TestSuite::Tester { explicit AnyImageConverterTest(); - void convert(); - void detect(); + void convert2D(); + void convertCompressed2D(); + void detect2D(); + void detectCompressed2D(); - void unknown(); + void unknown2D(); + void unknownCompressed2D(); - void propagateFlags(); + void propagateFlags2D(); + void propagateFlagsCompressed2D(); /* Explicitly forbid system-wide plugin dependencies */ PluginManager::Manager _manager{"nonexistent"}; @@ -76,15 +80,21 @@ constexpr struct { }; AnyImageConverterTest::AnyImageConverterTest() { - addInstancedTests({&AnyImageConverterTest::convert}, + addInstancedTests({&AnyImageConverterTest::convert2D}, Containers::arraySize(ConvertData)); - addInstancedTests({&AnyImageConverterTest::detect}, + addTests({&AnyImageConverterTest::convertCompressed2D}); + + addInstancedTests({&AnyImageConverterTest::detect2D}, Containers::arraySize(DetectData)); - addTests({&AnyImageConverterTest::unknown, + addTests({&AnyImageConverterTest::detectCompressed2D, + + &AnyImageConverterTest::unknown2D, + &AnyImageConverterTest::unknownCompressed2D, - &AnyImageConverterTest::propagateFlags}); + &AnyImageConverterTest::propagateFlags2D, + &AnyImageConverterTest::propagateFlagsCompressed2D}); /* Load the plugin directly from the build tree. Otherwise it's static and already loaded. */ @@ -108,7 +118,7 @@ constexpr const char Data[] = { const ImageView2D Image{PixelFormat::RGB8Unorm, {2, 3}, Data}; -void AnyImageConverterTest::convert() { +void AnyImageConverterTest::convert2D() { auto&& data = ConvertData[testCaseInstanceId()]; setTestCaseDescription(data.name); @@ -126,7 +136,11 @@ void AnyImageConverterTest::convert() { CORRADE_VERIFY(Utility::Directory::exists(filename)); } -void AnyImageConverterTest::detect() { +void AnyImageConverterTest::convertCompressed2D() { + CORRADE_SKIP("No file formats to store compressed data yet."); +} + +void AnyImageConverterTest::detect2D() { auto&& data = DetectData[testCaseInstanceId()]; setTestCaseDescription(data.name); @@ -145,7 +159,11 @@ void AnyImageConverterTest::detect() { #endif } -void AnyImageConverterTest::unknown() { +void AnyImageConverterTest::detectCompressed2D() { + CORRADE_SKIP("No file formats to store compressed data yet."); +} + +void AnyImageConverterTest::unknown2D() { std::ostringstream output; Error redirectError{&output}; @@ -155,7 +173,11 @@ void AnyImageConverterTest::unknown() { CORRADE_COMPARE(output.str(), "Trade::AnyImageConverter::convertToFile(): cannot determine the format of image.xcf\n"); } -void AnyImageConverterTest::propagateFlags() { +void AnyImageConverterTest::unknownCompressed2D() { + CORRADE_SKIP("No file formats to store compressed data yet."); +} + +void AnyImageConverterTest::propagateFlags2D() { if(!(_manager.loadState("TgaImageConverter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("TgaImageConverter plugin not enabled, cannot test"); @@ -178,6 +200,10 @@ void AnyImageConverterTest::propagateFlags() { "Trade::TgaImageConverter::convertToData(): converting from RGB to BGR\n"); } +void AnyImageConverterTest::propagateFlagsCompressed2D() { + CORRADE_SKIP("No file formats to store compressed data yet."); +} + }}}} CORRADE_TEST_MAIN(Magnum::Trade::Test::AnyImageConverterTest) From 50e75f90c86ddfb5def1d16db4eb7e4d0940e832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 22 Jun 2021 18:38:09 +0200 Subject: [PATCH 83/93] AnyImageConverter: rename a test macro for clarity. --- .../AnyImageConverter/Test/AnyImageConverterTest.cpp | 6 +++--- src/MagnumPlugins/AnyImageConverter/Test/CMakeLists.txt | 4 ++-- src/MagnumPlugins/AnyImageConverter/Test/configure.h.cmake | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/MagnumPlugins/AnyImageConverter/Test/AnyImageConverterTest.cpp b/src/MagnumPlugins/AnyImageConverter/Test/AnyImageConverterTest.cpp index c35952eaa..07c14bb61 100644 --- a/src/MagnumPlugins/AnyImageConverter/Test/AnyImageConverterTest.cpp +++ b/src/MagnumPlugins/AnyImageConverter/Test/AnyImageConverterTest.cpp @@ -107,7 +107,7 @@ AnyImageConverterTest::AnyImageConverterTest() { #endif /* Create the output directory if it doesn't exist yet */ - CORRADE_INTERNAL_ASSERT_OUTPUT(Utility::Directory::mkpath(ANYIMAGECONVERTER_TEST_DIR)); + CORRADE_INTERNAL_ASSERT_OUTPUT(Utility::Directory::mkpath(ANYIMAGECONVERTER_TEST_OUTPUT_DIR)); } constexpr const char Data[] = { @@ -125,7 +125,7 @@ void AnyImageConverterTest::convert2D() { if(!(_manager.loadState("TgaImageConverter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("TgaImageConverter plugin not enabled, cannot test"); - const std::string filename = Utility::Directory::join(ANYIMAGECONVERTER_TEST_DIR, data.filename); + const std::string filename = Utility::Directory::join(ANYIMAGECONVERTER_TEST_OUTPUT_DIR, data.filename); if(Utility::Directory::exists(filename)) CORRADE_VERIFY(Utility::Directory::rm(filename)); @@ -181,7 +181,7 @@ void AnyImageConverterTest::propagateFlags2D() { if(!(_manager.loadState("TgaImageConverter") & PluginManager::LoadState::Loaded)) CORRADE_SKIP("TgaImageConverter plugin not enabled, cannot test"); - const std::string filename = Utility::Directory::join(ANYIMAGECONVERTER_TEST_DIR, "output.tga"); + const std::string filename = Utility::Directory::join(ANYIMAGECONVERTER_TEST_OUTPUT_DIR, "output.tga"); if(Utility::Directory::exists(filename)) CORRADE_VERIFY(Utility::Directory::rm(filename)); diff --git a/src/MagnumPlugins/AnyImageConverter/Test/CMakeLists.txt b/src/MagnumPlugins/AnyImageConverter/Test/CMakeLists.txt index e7b665bb9..86a88dded 100644 --- a/src/MagnumPlugins/AnyImageConverter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/AnyImageConverter/Test/CMakeLists.txt @@ -24,9 +24,9 @@ # if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) - set(ANYIMAGECONVERTER_TEST_DIR "write") + set(ANYIMAGECONVERTER_TEST_OUTPUT_DIR "write") else() - set(ANYIMAGECONVERTER_TEST_DIR ${CMAKE_CURRENT_BINARY_DIR}) + set(ANYIMAGECONVERTER_TEST_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) endif() # CMake before 3.8 has broken $ expressions for iOS (see diff --git a/src/MagnumPlugins/AnyImageConverter/Test/configure.h.cmake b/src/MagnumPlugins/AnyImageConverter/Test/configure.h.cmake index a64fed90f..05a19d408 100644 --- a/src/MagnumPlugins/AnyImageConverter/Test/configure.h.cmake +++ b/src/MagnumPlugins/AnyImageConverter/Test/configure.h.cmake @@ -25,4 +25,4 @@ #cmakedefine ANYIMAGECONVERTER_PLUGIN_FILENAME "${ANYIMAGECONVERTER_PLUGIN_FILENAME}" #cmakedefine TGAIMAGECONVERTER_PLUGIN_FILENAME "${TGAIMAGECONVERTER_PLUGIN_FILENAME}" -#define ANYIMAGECONVERTER_TEST_DIR "${ANYIMAGECONVERTER_TEST_DIR}" +#define ANYIMAGECONVERTER_TEST_OUTPUT_DIR "${ANYIMAGECONVERTER_TEST_OUTPUT_DIR}" From 3452fd7a24c1d6572ba827a68ffc9b77a534adaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 23 Jun 2021 18:27:57 +0200 Subject: [PATCH 84/93] AnyShaderConverter: adapt tests to GlslangShaderCovnerter updates. --- .../AnyShaderConverter/Test/AnyConverterTest.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp index 498ea94cc..08e862f5d 100644 --- a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp +++ b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp @@ -877,7 +877,7 @@ void AnyConverterTest::convertFileToFilePropagateOptimization() { Error redirectError{&out}; CORRADE_VERIFY(!converter->convertFileToFile(Stage::Fragment, Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.spv"), Utility::Directory::join(ANYSHADERCONVERTER_TEST_OUTPUT_DIR, "file.spv"))); CORRADE_COMPARE(out.str(), - "ShaderTools::SpirvToolsConverter::convertDataToData(): optimization level should be 0, 1, s, legalizeHlsl, vulkanToWebGpu, webGpuToVulkan or empty but got 2\n"); + "ShaderTools::SpirvToolsConverter::convertDataToData(): optimization level should be 0, 1, s, legalizeHlsl or empty but got 2\n"); } void AnyConverterTest::convertFileToData() { @@ -1188,7 +1188,7 @@ void AnyConverterTest::convertFileToDataPropagateOptimization() { Error redirectError{&out}; CORRADE_VERIFY(!converter->convertFileToData(Stage::Fragment, Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.spv"))); CORRADE_COMPARE(out.str(), - "ShaderTools::SpirvToolsConverter::convertDataToData(): optimization level should be 0, 1, s, legalizeHlsl, vulkanToWebGpu, webGpuToVulkan or empty but got 2\n"); + "ShaderTools::SpirvToolsConverter::convertDataToData(): optimization level should be 0, 1, s, legalizeHlsl or empty but got 2\n"); } void AnyConverterTest::convertDataToData() { @@ -1506,7 +1506,7 @@ void AnyConverterTest::convertDataToDataPropagateOptimization() { Error redirectError{&out}; CORRADE_VERIFY(!converter->convertDataToData(Stage::Fragment, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.spv")))); CORRADE_COMPARE(out.str(), - "ShaderTools::SpirvToolsConverter::convertDataToData(): optimization level should be 0, 1, s, legalizeHlsl, vulkanToWebGpu, webGpuToVulkan or empty but got 2\n"); + "ShaderTools::SpirvToolsConverter::convertDataToData(): optimization level should be 0, 1, s, legalizeHlsl or empty but got 2\n"); } void AnyConverterTest::detectValidate() { From 23712c414ee00614512cb2ab322420073ca94490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 23 Jun 2021 18:28:26 +0200 Subject: [PATCH 85/93] AnyShaderConverter: assert the metadata validity always. Practically the metadata shouldn't be null if we managed to load the plugin already, so this was probably harmless. But dumb. --- .../AnyShaderConverter/AnyConverter.cpp | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp index 274334d83..130527699 100644 --- a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp +++ b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp @@ -190,11 +190,11 @@ std::pair AnyConverter::doValidateFile(const Stage sta Error{} << "ShaderTools::AnyConverter::validateFile(): cannot load the" << plugin << "plugin"; return {}; } - PluginManager::PluginMetadata* metadata = manager()->metadata(plugin); + const PluginManager::PluginMetadata* const metadata = manager()->metadata(plugin); + CORRADE_INTERNAL_ASSERT(metadata); if(flags() & ConverterFlag::Verbose) { Debug d; d << "ShaderTools::AnyConverter::validateFile(): using" << plugin; - CORRADE_INTERNAL_ASSERT(metadata); if(plugin != metadata->name()) d << "(provided by" << metadata->name() << Debug::nospace << ")"; } @@ -243,11 +243,12 @@ std::pair AnyConverter::doValidateData(const Stage sta Error{} << "ShaderTools::AnyConverter::validateData(): cannot load the" << plugin << "plugin"; return {}; } - PluginManager::PluginMetadata* metadata = manager()->metadata(plugin); + + const PluginManager::PluginMetadata* const metadata = manager()->metadata(plugin); + CORRADE_INTERNAL_ASSERT(metadata); if(flags() & ConverterFlag::Verbose) { Debug d; d << "ShaderTools::AnyConverter::validateData(): using" << plugin; - CORRADE_INTERNAL_ASSERT(metadata); if(plugin != metadata->name()) d << "(provided by" << metadata->name() << Debug::nospace << ")"; } @@ -307,11 +308,12 @@ bool AnyConverter::doConvertFileToFile(const Stage stage, const Containers::Stri Error{} << "ShaderTools::AnyConverter::convertFileToFile(): cannot load the" << plugin << "plugin"; return {}; } - PluginManager::PluginMetadata* metadata = manager()->metadata(plugin); + + const PluginManager::PluginMetadata* const metadata = manager()->metadata(plugin); + CORRADE_INTERNAL_ASSERT(metadata); if(flags() & ConverterFlag::Verbose) { Debug d; d << "ShaderTools::AnyConverter::convertFileToFile(): using" << plugin; - CORRADE_INTERNAL_ASSERT(metadata); if(plugin != metadata->name()) d << "(provided by" << metadata->name() << Debug::nospace << ")"; } @@ -388,11 +390,12 @@ Containers::Array AnyConverter::doConvertFileToData(const Stage stage, con Error{} << "ShaderTools::AnyConverter::convertFileToData(): cannot load the" << plugin << "plugin"; return {}; } - PluginManager::PluginMetadata* metadata = manager()->metadata(plugin); + + const PluginManager::PluginMetadata* const metadata = manager()->metadata(plugin); + CORRADE_INTERNAL_ASSERT(metadata); if(flags() & ConverterFlag::Verbose) { Debug d; d << "ShaderTools::AnyConverter::convertFileToData(): using" << plugin; - CORRADE_INTERNAL_ASSERT(metadata); if(plugin != metadata->name()) d << "(provided by" << metadata->name() << Debug::nospace << ")"; } @@ -467,11 +470,12 @@ Containers::Array AnyConverter::doConvertDataToData(const Stage stage, con Error{} << "ShaderTools::AnyConverter::convertDataToData(): cannot load the" << plugin << "plugin"; return {}; } - PluginManager::PluginMetadata* metadata = manager()->metadata(plugin); + + const PluginManager::PluginMetadata* const metadata = manager()->metadata(plugin); + CORRADE_INTERNAL_ASSERT(metadata); if(flags() & ConverterFlag::Verbose) { Debug d; d << "ShaderTools::AnyConverter::convertDataToData(): using" << plugin; - CORRADE_INTERNAL_ASSERT(metadata); if(plugin != metadata->name()) d << "(provided by" << metadata->name() << Debug::nospace << ")"; } From 4d61cda4a37677f7cebee4fc031a10e6cf95a986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 24 Jun 2021 14:28:47 +0200 Subject: [PATCH 86/93] AnyImageImporter: don't expect metadata to be present too early. The check that the plugin is loadable should happen before this, not after. All other importer plugins do that already. --- .../AnyImageImporter/AnyImageImporter.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp b/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp index a458087d0..786dd3ff0 100644 --- a/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp +++ b/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp @@ -118,6 +118,12 @@ void AnyImageImporter::doOpenFile(const std::string& filename) { Error{} << "Trade::AnyImageImporter::openFile(): cannot determine the format of" << filename; return; } + + /* Try to load the plugin */ + if(!(manager()->load(plugin) & PluginManager::LoadState::Loaded)) { + Error{} << "Trade::AnyImageImporter::openFile(): cannot load the" << plugin << "plugin"; + return; + } if(flags() & ImporterFlag::Verbose) { Debug d; d << "Trade::AnyImageImporter::openFile(): using" << plugin; @@ -127,12 +133,6 @@ void AnyImageImporter::doOpenFile(const std::string& filename) { d << "(provided by" << metadata->name() << Debug::nospace << ")"; } - /* Try to load the plugin */ - if(!(manager()->load(plugin) & PluginManager::LoadState::Loaded)) { - Error{} << "Trade::AnyImageImporter::openFile(): cannot load the" << plugin << "plugin"; - return; - } - /* Instantiate the plugin, propagate flags */ Containers::Pointer importer = static_cast*>(manager())->instantiate(plugin); importer->setFlags(flags()); From e6d673c279ac8a7cc067899adb6e52048bc40660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 24 Jun 2021 15:18:44 +0200 Subject: [PATCH 87/93] Propagate configuration options in Any* plugins. Minor but very important convenience feature, especially useful when dealing with command-line apps. This now works: magnum-imageconverter a.png a.jpg -c jpegQuality=0.75 The AnyImageConverter gets the jpegQuality option and then automatically propagates it to the concrete plugin (which is either JpegImageConverter or StbImageConverter), possibly warning in case the target plugin doesn't recognize given option (i.e., doesn't list it in its default configuration). Previously the user had to always specify a concrete converter implementation using -C, which was rather annoying and nonintuitive. --- doc/changelog.dox | 10 + .../Implementation/converterUtilities.h | 10 +- src/Magnum/MeshTools/sceneconverter.cpp | 4 +- src/Magnum/ShaderTools/shaderconverter.cpp | 2 +- src/Magnum/Trade/imageconverter.cpp | 4 +- .../AnyAudioImporter/AnyImporter.cpp | 12 +- .../AnyAudioImporter/AnyImporter.h | 12 + .../Test/AnyAudioImporterTest.cpp | 26 +- .../AnyImageConverter/AnyImageConverter.cpp | 9 +- .../AnyImageConverter/AnyImageConverter.h | 11 + .../Test/AnyImageConverterTest.cpp | 66 +++- .../AnyImageConverter/Test/CMakeLists.txt | 6 +- .../AnyImageConverter/Test/configure.h.cmake | 15 + .../AnyImageImporter/AnyImageImporter.cpp | 17 +- .../AnyImageImporter/AnyImageImporter.h | 14 + .../Test/AnyImageImporterTest.cpp | 78 ++++- .../AnyImageImporter/Test/CMakeLists.txt | 5 +- .../AnyImageImporter/Test/configure.h.cmake | 15 + .../Test/depth32f-custom-channels.exr | Bin 0 -> 327 bytes .../AnySceneConverter/AnySceneConverter.cpp | 9 +- .../AnySceneConverter/AnySceneConverter.h | 11 + .../Test/AnySceneConverterTest.cpp | 68 ++++- .../AnySceneConverter/Test/CMakeLists.txt | 5 +- .../AnySceneConverter/Test/configure.h.cmake | 1 + .../AnySceneConverter/Test/objectid.ply | Bin 0 -> 249 bytes .../AnySceneImporter/AnySceneImporter.cpp | 9 +- .../AnySceneImporter/AnySceneImporter.h | 15 + .../Test/AnySceneImporterTest.cpp | 64 +++- .../AnyShaderConverter/AnyConverter.cpp | 17 ++ .../AnyShaderConverter/AnyConverter.h | 18 ++ .../Test/AnyConverterTest.cpp | 284 +++++++++++++++++- .../AnyShaderConverter/Test/CMakeLists.txt | 3 +- .../Test/version-not-first.glsl | 4 + .../Implementation/propagateConfiguration.h | 69 +++++ 34 files changed, 863 insertions(+), 30 deletions(-) create mode 100644 src/MagnumPlugins/AnyImageImporter/Test/depth32f-custom-channels.exr create mode 100644 src/MagnumPlugins/AnySceneConverter/Test/objectid.ply create mode 100644 src/MagnumPlugins/AnyShaderConverter/Test/version-not-first.glsl create mode 100644 src/MagnumPlugins/Implementation/propagateConfiguration.h diff --git a/doc/changelog.dox b/doc/changelog.dox index 4244c5844..f793a0160 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -336,6 +336,16 @@ See also: @subsubsection changelog-latest-changes-trade Trade library - Recognizing TIFF file header magic in @ref Trade::AnyImageImporter "AnyImageImporter" +- @ref Audio::AnyImporter "AnyAudioImporter", + @relativeref{Trade,AnyImageImporter}, @relativeref{Trade,AnyImageConverter}, + @relativeref{Trade,AnySceneImporter}, @relativeref{Trade,AnySceneConverter} + and @ref ShaderTools::AnyConverter "AnyShaderConverter" are now capable of + propagating configuration options to the concrete plugin, which is useful + mainly when using @ref magnum-imageconverter and other utilities as you can + now specify just the `-i` / `-c` options without having to specify which + plugin to apply the option to with `-I` / `-C`. For better usability, the + plugins also warn if the user specifies and option that is not present in + the target implementation. - Added @ref Trade::PhongMaterialData::hasCommonTextureTransformation(), @ref Trade::PhongMaterialData::ambientTextureMatrix(), @ref Trade::PhongMaterialData::diffuseTextureMatrix(), diff --git a/src/Magnum/Implementation/converterUtilities.h b/src/Magnum/Implementation/converterUtilities.h index 906e8ac63..b86090fb1 100644 --- a/src/Magnum/Implementation/converterUtilities.h +++ b/src/Magnum/Implementation/converterUtilities.h @@ -38,7 +38,7 @@ namespace Magnum { namespace Implementation { /* Used only in executables where we don't want it to be exported */ namespace { -void setOptions(PluginManager::AbstractPlugin& plugin, const std::string& options) { +void setOptions(PluginManager::AbstractPlugin& plugin, const std::string& anyPluginName, const std::string& options) { for(const std::string& option: Utility::String::splitWithoutEmptyParts(options, ',')) { auto keyValue = Utility::String::partition(option, '='); Utility::String::trimInPlace(keyValue[0]); @@ -60,8 +60,12 @@ void setOptions(PluginManager::AbstractPlugin& plugin, const std::string& option /* Provide a warning message in case the plugin doesn't define given option in its default config. The plugin is not *required* to have those tho (could be backward compatibility entries, for example), so - not an error. */ - if(groupNotRecognized || !group->hasValue(keyParts.back())) { + not an error. + + If it's an Any* plugin, then this check is provided by it directly, + and since the Any* plugin obviously don't expose the options of the concrete plugins, this warning would fire for them always, which + wouldn't help anything. */ + if((groupNotRecognized || !group->hasValue(keyParts.back())) && plugin.plugin() != anyPluginName) { Warning{} << "Option" << keyValue[0] << "not recognized by" << plugin.plugin(); } diff --git a/src/Magnum/MeshTools/sceneconverter.cpp b/src/Magnum/MeshTools/sceneconverter.cpp index be9639758..e7c814b0c 100644 --- a/src/Magnum/MeshTools/sceneconverter.cpp +++ b/src/Magnum/MeshTools/sceneconverter.cpp @@ -263,7 +263,7 @@ used.)") /* Set options, if passed */ if(args.isSet("verbose")) importer->addFlags(Trade::ImporterFlag::Verbose); - Implementation::setOptions(*importer, args.value("importer-options")); + Implementation::setOptions(*importer, "AnySceneImporter", args.value("importer-options")); std::chrono::high_resolution_clock::duration importTime; @@ -823,7 +823,7 @@ used.)") /* Set options, if passed */ if(args.isSet("verbose")) converter->addFlags(Trade::SceneConverterFlag::Verbose); if(i < args.arrayValueCount("converter-options")) - Implementation::setOptions(*converter, args.arrayValue("converter-options", i)); + Implementation::setOptions(*converter, "AnySceneConverter", args.arrayValue("converter-options", i)); /* This is the last --converter (or the implicit AnySceneConverter at the end), output to a file and exit the loop */ diff --git a/src/Magnum/ShaderTools/shaderconverter.cpp b/src/Magnum/ShaderTools/shaderconverter.cpp index d0c2c0233..55d1c252b 100644 --- a/src/Magnum/ShaderTools/shaderconverter.cpp +++ b/src/Magnum/ShaderTools/shaderconverter.cpp @@ -366,7 +366,7 @@ see documentation of a particular converter for more information.)") /* Set options if passed */ if(i < args.arrayValueCount("converter-options")) - Implementation::setOptions(*converter, args.arrayValue("converter-options", i)); + Implementation::setOptions(*converter, "AnyShaderConverter", args.arrayValue("converter-options", i)); /* Parse format, if passed. If --info is desired, implicitly set the output format to SPIR-V */ diff --git a/src/Magnum/Trade/imageconverter.cpp b/src/Magnum/Trade/imageconverter.cpp index 17a304e5a..b261efd50 100644 --- a/src/Magnum/Trade/imageconverter.cpp +++ b/src/Magnum/Trade/imageconverter.cpp @@ -245,7 +245,7 @@ key=true; configuration subgroups are delimited with /.)") /* Set options, if passed */ if(args.isSet("verbose")) importer->addFlags(Trade::ImporterFlag::Verbose); - Implementation::setOptions(*importer, args.value("importer-options")); + Implementation::setOptions(*importer, "AnyImageImporter", args.value("importer-options")); /* Print image info, if requested */ if(args.isSet("info")) { @@ -329,7 +329,7 @@ key=true; configuration subgroups are delimited with /.)") /* Set options, if passed */ if(args.isSet("verbose")) converter->addFlags(Trade::ImageConverterFlag::Verbose); - Implementation::setOptions(*converter, args.value("converter-options")); + Implementation::setOptions(*converter, "AnyImageConverter", args.value("converter-options")); /* Save output file */ if(!converter->convertToFile(*image, output)) { diff --git a/src/MagnumPlugins/AnyAudioImporter/AnyImporter.cpp b/src/MagnumPlugins/AnyAudioImporter/AnyImporter.cpp index 677c6f3f9..62334ea3d 100644 --- a/src/MagnumPlugins/AnyAudioImporter/AnyImporter.cpp +++ b/src/MagnumPlugins/AnyAudioImporter/AnyImporter.cpp @@ -27,10 +27,13 @@ #include #include +#include #include #include #include +#include "MagnumPlugins/Implementation/propagateConfiguration.h" + namespace Magnum { namespace Audio { AnyImporter::AnyImporter(PluginManager::Manager& manager): AbstractImporter{manager} {} @@ -74,9 +77,16 @@ void AnyImporter::doOpenFile(const std::string& filename) { return; } + /* Instantiate the plugin */ + Containers::Pointer importer = static_cast*>(manager())->instantiate(plugin); + + /* Propagate configuration */ + const PluginManager::PluginMetadata* const metadata = manager()->metadata(plugin); + CORRADE_INTERNAL_ASSERT(metadata); + Magnum::Implementation::propagateConfiguration("Audio::AnyImporter::openFile():", {}, metadata->name(), configuration(), importer->configuration()); + /* Try to open the file (error output should be printed by the plugin itself) */ - Containers::Pointer importer = static_cast*>(manager())->instantiate(plugin); if(!importer->openFile(filename)) return; /* Success, save the instance */ diff --git a/src/MagnumPlugins/AnyAudioImporter/AnyImporter.h b/src/MagnumPlugins/AnyAudioImporter/AnyImporter.h index 25e1ae371..e54d85f7c 100644 --- a/src/MagnumPlugins/AnyAudioImporter/AnyImporter.h +++ b/src/MagnumPlugins/AnyAudioImporter/AnyImporter.h @@ -99,6 +99,18 @@ target_link_libraries(your-app PRIVATE Magnum::AnyAudioImporter) @endcode See @ref building, @ref cmake and @ref plugins for more information. + +@section Audio-AnyImporter-proxy Interface proxying and option propagation + +On a call to @ref openFile(), a file format is detected from the extension and +a corresponding plugin is loaded. After that, options set through +@ref configuration() are propagated to the concrete implementation, with a +warning emitted in case given option is not present in the default +configuration of the target plugin. + +Calls to the @ref format(), @ref frequency() and @ref data() functions are then +proxied to the concrete implementation. The @ref close() function closes and +discards the internally instantiated plugin; @ref isOpened() works as usual. */ class MAGNUM_ANYAUDIOIMPORTER_EXPORT AnyImporter: public AbstractImporter { public: diff --git a/src/MagnumPlugins/AnyAudioImporter/Test/AnyAudioImporterTest.cpp b/src/MagnumPlugins/AnyAudioImporter/Test/AnyAudioImporterTest.cpp index a898cf772..52a3aac32 100644 --- a/src/MagnumPlugins/AnyAudioImporter/Test/AnyAudioImporterTest.cpp +++ b/src/MagnumPlugins/AnyAudioImporter/Test/AnyAudioImporterTest.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,9 @@ struct AnyImporterTest: TestSuite::Tester { void unknown(); + void propagateConfiguration(); + void propagateConfigurationUnknown(); + /* Explicitly forbid system-wide plugin dependencies */ PluginManager::Manager _manager{"nonexistent"}; }; @@ -72,7 +76,10 @@ AnyImporterTest::AnyImporterTest() { addInstancedTests({&AnyImporterTest::detect}, Containers::arraySize(DetectData)); - addTests({&AnyImporterTest::unknown}); + addTests({&AnyImporterTest::unknown, + + &AnyImporterTest::propagateConfiguration, + &AnyImporterTest::propagateConfigurationUnknown}); /* Load the plugin directly from the build tree. Otherwise it's static and already loaded. */ @@ -133,6 +140,23 @@ void AnyImporterTest::unknown() { CORRADE_COMPARE(output.str(), "Audio::AnyImporter::openFile(): cannot determine the format of sound.mid\n"); } +void AnyImporterTest::propagateConfiguration() { + CORRADE_SKIP("No importer has any configuration options to test."); +} + +void AnyImporterTest::propagateConfigurationUnknown() { + if(!(_manager.loadState("WavAudioImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("WavAudioImporter plugin not enabled, cannot test"); + + Containers::Pointer importer = _manager.instantiate("AnyAudioImporter"); + importer->configuration().setValue("noSuchOption", "isHere"); + + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(importer->openFile(WAV_FILE)); + CORRADE_COMPARE(out.str(), "Audio::AnyImporter::openFile(): option noSuchOption not recognized by WavAudioImporter\n"); +} + }}}} CORRADE_TEST_MAIN(Magnum::Audio::Test::AnyImporterTest) diff --git a/src/MagnumPlugins/AnyImageConverter/AnyImageConverter.cpp b/src/MagnumPlugins/AnyImageConverter/AnyImageConverter.cpp index c92cbae8a..5ea4570f4 100644 --- a/src/MagnumPlugins/AnyImageConverter/AnyImageConverter.cpp +++ b/src/MagnumPlugins/AnyImageConverter/AnyImageConverter.cpp @@ -34,6 +34,7 @@ #include #include "Magnum/Trade/ImageData.h" +#include "MagnumPlugins/Implementation/propagateConfiguration.h" namespace Magnum { namespace Trade { @@ -84,11 +85,12 @@ bool AnyImageConverter::doConvertToFile(const ImageView2D& image, const Containe Error{} << "Trade::AnyImageConverter::convertToFile(): cannot load the" << plugin << "plugin"; return false; } + + const PluginManager::PluginMetadata* const metadata = manager()->metadata(plugin); + CORRADE_INTERNAL_ASSERT(metadata); if(flags() & ImageConverterFlag::Verbose) { Debug d; d << "Trade::AnyImageConverter::convertToFile(): using" << plugin; - PluginManager::PluginMetadata* metadata = manager()->metadata(plugin); - CORRADE_INTERNAL_ASSERT(metadata); if(plugin != metadata->name()) d << "(provided by" << metadata->name() << Debug::nospace << ")"; } @@ -97,6 +99,9 @@ bool AnyImageConverter::doConvertToFile(const ImageView2D& image, const Containe Containers::Pointer converter = static_cast*>(manager())->instantiate(plugin); converter->setFlags(flags()); + /* Propagate configuration */ + Magnum::Implementation::propagateConfiguration("Trade::AnyImageConverter::convertToFile():", {}, metadata->name(), configuration(), converter->configuration()); + /* Try to convert the file (error output should be printed by the plugin itself) */ return converter->convertToFile(image, filename); diff --git a/src/MagnumPlugins/AnyImageConverter/AnyImageConverter.h b/src/MagnumPlugins/AnyImageConverter/AnyImageConverter.h index c2133b902..ab3afded3 100644 --- a/src/MagnumPlugins/AnyImageConverter/AnyImageConverter.h +++ b/src/MagnumPlugins/AnyImageConverter/AnyImageConverter.h @@ -104,6 +104,17 @@ target_link_libraries(your-app PRIVATE Magnum::AnyImageConverter) See @ref building, @ref cmake, @ref plugins and @ref file-formats for more information. + +@section Trade-AnyImageConverter-proxy Interface proxying and option propagation + +On a call to @ref convertToFile(), a target file format is detected from the +extension and a corresponding plugin is loaded. After that, flags set via +@ref setFlags() and options set through @ref configuration() are propagated to +the concrete implementation, with a warning emitted in case given option is not +present in the default configuration of the target plugin. + +The output of the @ref convertToFile() function called on the concrete +implementation is then proxied back. */ class MAGNUM_ANYIMAGECONVERTER_EXPORT AnyImageConverter: public AbstractImageConverter { public: diff --git a/src/MagnumPlugins/AnyImageConverter/Test/AnyImageConverterTest.cpp b/src/MagnumPlugins/AnyImageConverter/Test/AnyImageConverterTest.cpp index 07c14bb61..f518a5723 100644 --- a/src/MagnumPlugins/AnyImageConverter/Test/AnyImageConverterTest.cpp +++ b/src/MagnumPlugins/AnyImageConverter/Test/AnyImageConverterTest.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -53,6 +55,12 @@ struct AnyImageConverterTest: TestSuite::Tester { void propagateFlags2D(); void propagateFlagsCompressed2D(); + void propagateConfiguration2D(); + void propagateConfigurationUnknown2D(); + void propagateConfigurationCompressed2D(); + void propagateConfigurationCompressedUnknown2D(); + /* configuration propagation fully tested in AnySceneImporter, as there the + plugins have configuration subgroups as well */ /* Explicitly forbid system-wide plugin dependencies */ PluginManager::Manager _manager{"nonexistent"}; @@ -94,7 +102,11 @@ AnyImageConverterTest::AnyImageConverterTest() { &AnyImageConverterTest::unknownCompressed2D, &AnyImageConverterTest::propagateFlags2D, - &AnyImageConverterTest::propagateFlagsCompressed2D}); + &AnyImageConverterTest::propagateFlagsCompressed2D, + &AnyImageConverterTest::propagateConfiguration2D, + &AnyImageConverterTest::propagateConfigurationUnknown2D, + &AnyImageConverterTest::propagateConfigurationCompressed2D, + &AnyImageConverterTest::propagateConfigurationCompressedUnknown2D}); /* Load the plugin directly from the build tree. Otherwise it's static and already loaded. */ @@ -204,6 +216,58 @@ void AnyImageConverterTest::propagateFlagsCompressed2D() { CORRADE_SKIP("No file formats to store compressed data yet."); } +void AnyImageConverterTest::propagateConfiguration2D() { + PluginManager::Manager manager{MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR}; + #ifdef ANYIMAGECONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYIMAGECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.loadState("OpenExrImageConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("OpenExrImageConverter plugin can't be loaded."); + + const std::string filename = Utility::Directory::join(ANYIMAGECONVERTER_TEST_OUTPUT_DIR, "depth32f-custom-channels.exr"); + + if(Utility::Directory::exists(filename)) + CORRADE_VERIFY(Utility::Directory::rm(filename)); + + const Float Depth32fData[] = { + 0.125f, 0.250f, 0.375f, + 0.500f, 0.625f, 0.750f + }; + + const ImageView2D Depth32f{PixelFormat::Depth32F, {3, 2}, Depth32fData}; + + Containers::Pointer converter = manager.instantiate("AnyImageConverter"); + converter->configuration().setValue("layer", "left"); + converter->configuration().setValue("depth", "height"); + CORRADE_VERIFY(converter->convertToFile(Depth32f, filename)); + /* Compare to an expected output to ensure the custom channels names were + used */ + CORRADE_COMPARE_AS(filename, EXR_FILE, TestSuite::Compare::File); +} + +void AnyImageConverterTest::propagateConfigurationUnknown2D() { + if(!(_manager.loadState("TgaImageConverter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("TgaImageConverter plugin not enabled, cannot test"); + + /* Just test that the exported file exists */ + Containers::Pointer converter = _manager.instantiate("AnyImageConverter"); + converter->configuration().setValue("noSuchOption", "isHere"); + + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter->convertToFile(Image, Utility::Directory::join(ANYIMAGECONVERTER_TEST_OUTPUT_DIR, "output.tga"))); + CORRADE_COMPARE(out.str(), "Trade::AnyImageConverter::convertToFile(): option noSuchOption not recognized by TgaImageConverter\n"); +} + +void AnyImageConverterTest::propagateConfigurationCompressed2D() { + CORRADE_SKIP("No file formats to store compressed data yet."); +} + +void AnyImageConverterTest::propagateConfigurationCompressedUnknown2D() { + CORRADE_SKIP("No file formats to store compressed data yet."); +} + }}}} CORRADE_TEST_MAIN(Magnum::Trade::Test::AnyImageConverterTest) diff --git a/src/MagnumPlugins/AnyImageConverter/Test/CMakeLists.txt b/src/MagnumPlugins/AnyImageConverter/Test/CMakeLists.txt index 86a88dded..1a8d49da1 100644 --- a/src/MagnumPlugins/AnyImageConverter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/AnyImageConverter/Test/CMakeLists.txt @@ -25,8 +25,10 @@ if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) set(ANYIMAGECONVERTER_TEST_OUTPUT_DIR "write") + set(EXR_FILE depth32f-custom-channels.exr) else() set(ANYIMAGECONVERTER_TEST_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) + set(EXR_FILE ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/AnyImageImporter/Test/depth32f-custom-channels.exr) endif() # CMake before 3.8 has broken $ expressions for iOS (see @@ -47,7 +49,9 @@ file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) corrade_add_test(AnyImageConverterTest AnyImageConverterTest.cpp - LIBRARIES MagnumTrade) + LIBRARIES MagnumTrade + FILES + ../../AnyImageImporter/Test/depth32f-custom-channels.exr) target_include_directories(AnyImageConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) if(MAGNUM_ANYIMAGECONVERTER_BUILD_STATIC) target_link_libraries(AnyImageConverterTest PRIVATE AnyImageConverter) diff --git a/src/MagnumPlugins/AnyImageConverter/Test/configure.h.cmake b/src/MagnumPlugins/AnyImageConverter/Test/configure.h.cmake index 05a19d408..4719ac532 100644 --- a/src/MagnumPlugins/AnyImageConverter/Test/configure.h.cmake +++ b/src/MagnumPlugins/AnyImageConverter/Test/configure.h.cmake @@ -26,3 +26,18 @@ #cmakedefine ANYIMAGECONVERTER_PLUGIN_FILENAME "${ANYIMAGECONVERTER_PLUGIN_FILENAME}" #cmakedefine TGAIMAGECONVERTER_PLUGIN_FILENAME "${TGAIMAGECONVERTER_PLUGIN_FILENAME}" #define ANYIMAGECONVERTER_TEST_OUTPUT_DIR "${ANYIMAGECONVERTER_TEST_OUTPUT_DIR}" +#define EXR_FILE "${EXR_FILE}" + +#ifdef CORRADE_TARGET_WINDOWS +#ifdef CORRADE_IS_DEBUG_BUILD +#define MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_BINARY_INSTALL_DIR}" +#else +#define MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_BINARY_INSTALL_DIR}" +#endif +#else +#ifdef CORRADE_IS_DEBUG_BUILD +#define MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_LIBRARY_INSTALL_DIR}" +#else +#define MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_LIBRARY_INSTALL_DIR}" +#endif +#endif diff --git a/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp b/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp index 786dd3ff0..877852f55 100644 --- a/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp +++ b/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp @@ -35,6 +35,7 @@ #include #include "Magnum/Trade/ImageData.h" +#include "MagnumPlugins/Implementation/propagateConfiguration.h" namespace Magnum { namespace Trade { @@ -124,11 +125,12 @@ void AnyImageImporter::doOpenFile(const std::string& filename) { Error{} << "Trade::AnyImageImporter::openFile(): cannot load the" << plugin << "plugin"; return; } + + const PluginManager::PluginMetadata* const metadata = manager()->metadata(plugin); + CORRADE_INTERNAL_ASSERT(metadata); if(flags() & ImporterFlag::Verbose) { Debug d; d << "Trade::AnyImageImporter::openFile(): using" << plugin; - PluginManager::PluginMetadata* metadata = manager()->metadata(plugin); - CORRADE_INTERNAL_ASSERT(metadata); if(plugin != metadata->name()) d << "(provided by" << metadata->name() << Debug::nospace << ")"; } @@ -137,6 +139,9 @@ void AnyImageImporter::doOpenFile(const std::string& filename) { Containers::Pointer importer = static_cast*>(manager())->instantiate(plugin); importer->setFlags(flags()); + /* Propagate configuration */ + Magnum::Implementation::propagateConfiguration("Trade::AnyImageImporter::openFile():", {}, metadata->name(), configuration(), importer->configuration()); + /* Try to open the file (error output should be printed by the plugin itself) */ if(!importer->openFile(filename)) return; @@ -219,11 +224,12 @@ void AnyImageImporter::doOpenData(Containers::ArrayView data) { Error{} << "Trade::AnyImageImporter::openData(): cannot load the" << plugin << "plugin"; return; } + + const PluginManager::PluginMetadata* const metadata = manager()->metadata(plugin); + CORRADE_INTERNAL_ASSERT(metadata); if(flags() & ImporterFlag::Verbose) { Debug d; d << "Trade::AnyImageImporter::openData(): using" << plugin; - PluginManager::PluginMetadata* metadata = manager()->metadata(plugin); - CORRADE_INTERNAL_ASSERT(metadata); if(plugin != metadata->name()) d << "(provided by" << metadata->name() << Debug::nospace << ")"; } @@ -232,6 +238,9 @@ void AnyImageImporter::doOpenData(Containers::ArrayView data) { Containers::Pointer importer = static_cast*>(manager())->instantiate(plugin); importer->setFlags(flags()); + /* Propagate configuration */ + Magnum::Implementation::propagateConfiguration("Trade::AnyImageImporter::openData():", {}, metadata->name(), configuration(), importer->configuration()); + /* Try to open the file (error output should be printed by the plugin itself) */ if(!importer->openData(data)) return; diff --git a/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.h b/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.h index 92d6f332d..730adffca 100644 --- a/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.h +++ b/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.h @@ -127,6 +127,20 @@ target_link_libraries(your-app PRIVATE Magnum::AnyImageImporter) See @ref building, @ref cmake, @ref plugins and @ref file-formats for more information. + +@section Audio-AnyImageImporter-proxy Interface proxying and option propagation + +On a call to @ref openFile() / @ref openData(), a file format is detected from +the extension / file signature and a corresponding plugin is loaded. After +that, flags set via @ref setFlags() and options set through +@ref configuration() are propagated to the concrete implementation, with a +warning emitted in case given option is not present in the default +configuration of the target plugin. + +Calls to the @ref image2DCount(), @ref image2DLevelCount() and @ref image2D() +functions are then proxied to the concrete implementation. The @ref close() +function closes and discards the internally instantiated plugin; +@ref isOpened() works as usual. */ class MAGNUM_ANYIMAGEIMPORTER_EXPORT AnyImageImporter: public AbstractImporter { public: diff --git a/src/MagnumPlugins/AnyImageImporter/Test/AnyImageImporterTest.cpp b/src/MagnumPlugins/AnyImageImporter/Test/AnyImageImporterTest.cpp index 8179fdde4..d210a440c 100644 --- a/src/MagnumPlugins/AnyImageImporter/Test/AnyImageImporterTest.cpp +++ b/src/MagnumPlugins/AnyImageImporter/Test/AnyImageImporterTest.cpp @@ -28,10 +28,13 @@ #include #include #include +#include #include #include #include +#include "Magnum/ImageView.h" +#include "Magnum/DebugTools/CompareImage.h" #include "Magnum/Trade/AbstractImporter.h" #include "Magnum/Trade/ImageData.h" @@ -50,6 +53,10 @@ struct AnyImageImporterTest: TestSuite::Tester { void emptyData(); void propagateFlags(); + void propagateConfiguration(); + void propagateConfigurationUnknown(); + /* configuration propagation fully tested in AnySceneImporter, as there the + plugins have configuration subgroups as well */ /* Explicitly forbid system-wide plugin dependencies */ PluginManager::Manager _manager{"nonexistent"}; @@ -64,7 +71,7 @@ constexpr struct { const char* name; const char* filename; Containers::Optional>(*callback)(const std::string&, InputFileCallbackPolicy, Containers::Array&); - const char* verboseFunctionName; + const char* messageFunctionName; } LoadData[]{ {"TGA", TGA_FILE, nullptr, "openFile"}, {"TGA data", TGA_FILE, fileCallback, "openData"} @@ -116,6 +123,15 @@ const struct { {"TIFF, but no zero byte", "MM\xff\x2a"_s, "4d4dff2a"} }; +constexpr struct { + const char* name; + const char* filename; + Containers::Optional>(*callback)(const std::string&, InputFileCallbackPolicy, Containers::Array&); +} PropagateConfigurationData[]{ + {"EXR", EXR_FILE, nullptr}, + {"EXR data", EXR_FILE, fileCallback} +}; + AnyImageImporterTest::AnyImageImporterTest() { addInstancedTests({&AnyImageImporterTest::load}, Containers::arraySize(LoadData)); @@ -133,6 +149,12 @@ AnyImageImporterTest::AnyImageImporterTest() { addInstancedTests({&AnyImageImporterTest::propagateFlags}, Containers::arraySize(LoadData)); + addInstancedTests({&AnyImageImporterTest::propagateConfiguration}, + Containers::arraySize(PropagateConfigurationData)); + + addInstancedTests({&AnyImageImporterTest::propagateConfigurationUnknown}, + Containers::arraySize(LoadData)); + /* Load the plugin directly from the build tree. Otherwise it's static and already loaded. */ #ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME @@ -244,7 +266,59 @@ void AnyImageImporterTest::propagateFlags() { CORRADE_COMPARE(out.str(), Utility::formatString( "Trade::AnyImageImporter::{}(): using TgaImporter\n" "Trade::TgaImporter::image2D(): converting from BGR to RGB\n", - data.verboseFunctionName)); + data.messageFunctionName)); +} + +void AnyImageImporterTest::propagateConfiguration() { + auto&& data = PropagateConfigurationData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + PluginManager::Manager manager{MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR}; + #ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYIMAGEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.loadState("OpenExrImporter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("OpenExrImporter plugin can't be loaded."); + + Containers::Pointer importer = manager.instantiate("AnyImageImporter"); + importer->configuration().setValue("layer", "left"); + importer->configuration().setValue("depth", "height"); + + Containers::Array storage; + importer->setFileCallback(data.callback, storage); + CORRADE_VERIFY(importer->openFile(data.filename)); + + Containers::Optional image = importer->image2D(0); + CORRADE_VERIFY(image); + + /* Comparing image contents to verify the custom channels were used */ + const Float Depth32fData[] = { + 0.125f, 0.250f, 0.375f, + 0.500f, 0.625f, 0.750f + }; + const ImageView2D Depth32f{PixelFormat::Depth32F, {3, 2}, Depth32fData}; + CORRADE_COMPARE_AS(*image, Depth32f, + DebugTools::CompareImage); +} + +void AnyImageImporterTest::propagateConfigurationUnknown() { + auto&& data = LoadData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + if(!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("TgaImporter plugin not enabled, cannot test"); + + Containers::Pointer importer = _manager.instantiate("AnyImageImporter"); + importer->configuration().setValue("noSuchOption", "isHere"); + + Containers::Array storage; + importer->setFileCallback(data.callback, storage); + + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(importer->openFile(data.filename)); + CORRADE_COMPARE(out.str(), Utility::formatString("Trade::AnyImageImporter::{}(): option noSuchOption not recognized by TgaImporter\n", data.messageFunctionName)); } }}}} diff --git a/src/MagnumPlugins/AnyImageImporter/Test/CMakeLists.txt b/src/MagnumPlugins/AnyImageImporter/Test/CMakeLists.txt index 03a5c386a..1510bb328 100644 --- a/src/MagnumPlugins/AnyImageImporter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/AnyImageImporter/Test/CMakeLists.txt @@ -26,9 +26,11 @@ if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) set(TEST_FILE_DIR .) set(TGA_FILE rgb.tga) + set(EXR_FILE depth32f-custom-channels.exr) else() set(TEST_FILE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(TGA_FILE ${CMAKE_CURRENT_SOURCE_DIR}/rgb.tga) + set(EXR_FILE ${CMAKE_CURRENT_SOURCE_DIR}/depth32f-custom-channels.exr) endif() # CMake before 3.8 has broken $ expressions for iOS (see @@ -49,8 +51,9 @@ file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) corrade_add_test(AnyImageImporterTest AnyImageImporterTest.cpp - LIBRARIES MagnumTrade + LIBRARIES MagnumTrade MagnumDebugTools FILES + depth32f-custom-channels.exr gray.jpg image.exr image.tiff diff --git a/src/MagnumPlugins/AnyImageImporter/Test/configure.h.cmake b/src/MagnumPlugins/AnyImageImporter/Test/configure.h.cmake index 5380890bf..a07bc6682 100644 --- a/src/MagnumPlugins/AnyImageImporter/Test/configure.h.cmake +++ b/src/MagnumPlugins/AnyImageImporter/Test/configure.h.cmake @@ -27,3 +27,18 @@ #cmakedefine TGAIMPORTER_PLUGIN_FILENAME "${TGAIMPORTER_PLUGIN_FILENAME}" #define TGA_FILE "${TGA_FILE}" #define TEST_FILE_DIR "${TEST_FILE_DIR}" +#define EXR_FILE "${EXR_FILE}" + +#ifdef CORRADE_TARGET_WINDOWS +#ifdef CORRADE_IS_DEBUG_BUILD +#define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_DEBUG_BINARY_INSTALL_DIR}" +#else +#define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_RELEASE_BINARY_INSTALL_DIR}" +#endif +#else +#ifdef CORRADE_IS_DEBUG_BUILD +#define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_DEBUG_LIBRARY_INSTALL_DIR}" +#else +#define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_RELEASE_LIBRARY_INSTALL_DIR}" +#endif +#endif diff --git a/src/MagnumPlugins/AnyImageImporter/Test/depth32f-custom-channels.exr b/src/MagnumPlugins/AnyImageImporter/Test/depth32f-custom-channels.exr new file mode 100644 index 0000000000000000000000000000000000000000..02b41f97a7b676d8095418163ae15b534b4ea546 GIT binary patch literal 327 zcmaitJr2S!422&*10y2~5*r(c9-swr03;UX(74o6NUFvvuyHhwMnWs`vta4PPx0sH zX1VJ10pz8!mW&U^uoq0jpP@n)CGn{gL?%|R6LNR1YVw|){qhkQXeH{vR=W$1?wT;h zakr}&6)DZWGV1o1Gi+&BYpU^faUT_5$!vT@x$Kp&Lt&f}4C4&<%;z*n7 #include "Magnum/Trade/ImageData.h" +#include "MagnumPlugins/Implementation/propagateConfiguration.h" namespace Magnum { namespace Trade { @@ -67,11 +68,12 @@ bool AnySceneConverter::doConvertToFile(const MeshData& mesh, const Containers:: Error{} << "Trade::AnySceneConverter::convertToFile(): cannot load the" << plugin << "plugin"; return false; } + + const PluginManager::PluginMetadata* const metadata = manager()->metadata(plugin); + CORRADE_INTERNAL_ASSERT(metadata); if(flags() & SceneConverterFlag::Verbose) { Debug d; d << "Trade::AnySceneConverter::convertToFile(): using" << plugin; - PluginManager::PluginMetadata* metadata = manager()->metadata(plugin); - CORRADE_INTERNAL_ASSERT(metadata); if(plugin != metadata->name()) d << "(provided by" << metadata->name() << Debug::nospace << ")"; } @@ -80,6 +82,9 @@ bool AnySceneConverter::doConvertToFile(const MeshData& mesh, const Containers:: Containers::Pointer converter = static_cast*>(manager())->instantiate(plugin); converter->setFlags(flags()); + /* Propagate configuration */ + Magnum::Implementation::propagateConfiguration("Trade::AnySceneConverter::convertToFile():", {}, metadata->name(), configuration(), converter->configuration()); + /* Try to convert the file (error output should be printed by the plugin itself) */ return converter->convertToFile(mesh, filename); diff --git a/src/MagnumPlugins/AnySceneConverter/AnySceneConverter.h b/src/MagnumPlugins/AnySceneConverter/AnySceneConverter.h index 4dce14d30..82934b2d8 100644 --- a/src/MagnumPlugins/AnySceneConverter/AnySceneConverter.h +++ b/src/MagnumPlugins/AnySceneConverter/AnySceneConverter.h @@ -91,6 +91,17 @@ target_link_libraries(your-app PRIVATE Magnum::AnySceneConverter) See @ref building, @ref cmake, @ref plugins and @ref file-formats for more information. + +@section Trade-AnySceneConverter-proxy Interface proxying and option propagation + +On a call to @ref convertToFile(), a target file format is detected from the +extension and a corresponding plugin is loaded. After that, flags set via +@ref setFlags() and options set through @ref configuration() are propagated to +the concrete implementation, with a warning emitted in case given option is not +present in the default configuration of the target plugin. + +The output of the @ref convertToFile() function called on the concrete +implementation is then proxied back. */ class MAGNUM_ANYSCENECONVERTER_EXPORT AnySceneConverter: public AbstractSceneConverter { public: diff --git a/src/MagnumPlugins/AnySceneConverter/Test/AnySceneConverterTest.cpp b/src/MagnumPlugins/AnySceneConverter/Test/AnySceneConverterTest.cpp index 73c52eae1..3e2361688 100644 --- a/src/MagnumPlugins/AnySceneConverter/Test/AnySceneConverterTest.cpp +++ b/src/MagnumPlugins/AnySceneConverter/Test/AnySceneConverterTest.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,10 @@ struct AnySceneConverterTest: TestSuite::Tester { void unknown(); void propagateFlags(); + void propagateConfiguration(); + void propagateConfigurationUnknown(); + /* configuration propagation fully tested in AnySceneImporter, as there the + plugins have configuration subgroups as well */ /* Explicitly forbid system-wide plugin dependencies */ PluginManager::Manager _manager{"nonexistent"}; @@ -72,7 +77,9 @@ AnySceneConverterTest::AnySceneConverterTest() { addTests({&AnySceneConverterTest::unknown, - &AnySceneConverterTest::propagateFlags}); + &AnySceneConverterTest::propagateFlags, + &AnySceneConverterTest::propagateConfiguration, + &AnySceneConverterTest::propagateConfigurationUnknown}); /* Load the plugin directly from the build tree. Otherwise it's static and already loaded. */ @@ -180,6 +187,65 @@ void AnySceneConverterTest::propagateFlags() { CORRADE_SKIP("No plugin with verbose output available to test flag propagation."); } +void AnySceneConverterTest::propagateConfiguration() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR}; + #ifdef ANYSCENECONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSCENECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.loadState("StanfordSceneConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("StanfordSceneConverter plugin can't be loaded."); + + const std::string filename = Utility::Directory::join(ANYSCENECONVERTER_TEST_OUTPUT_DIR, "file.ply"); + + const struct Data { + Vector3 position; + UnsignedInt objectId; + } data[] { + {{-0.5f, -0.5f, 0.0f}, 4678}, + {{ 0.5f, -0.5f, 0.0f}, 3232}, + {{ 0.0f, 0.5f, 0.0f}, 1536} + }; + const Trade::MeshData mesh{MeshPrimitive::Triangles, {}, data, { + Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(data).slice(&Data::position)}, + Trade::MeshAttributeData{Trade::MeshAttribute::ObjectId, Containers::stridedArrayView(data).slice(&Data::objectId)}, + }}; + + Containers::Pointer converter = manager.instantiate("AnySceneConverter"); + converter->configuration().setValue("objectIdAttribute", "OID"); + CORRADE_VERIFY(converter->convertToFile(mesh, filename)); + /* Compare to an expected output to ensure the custom attribute name was + used */ + CORRADE_COMPARE_AS(filename, PLY_OBJECTID_FILE, TestSuite::Compare::File); +} + +void AnySceneConverterTest::propagateConfigurationUnknown() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR}; + #ifdef ANYSCENECONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSCENECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.loadState("StanfordSceneConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("StanfordSceneConverter plugin can't be loaded."); + + const Vector3 positions[] { + {-0.5f, -0.5f, 0.0f}, + { 0.5f, -0.5f, 0.0f}, + { 0.0f, 0.5f, 0.0f} + }; + const Trade::MeshData mesh{MeshPrimitive::Triangles, {}, positions, { + Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::arrayView(positions)} + }}; + + Containers::Pointer converter = manager.instantiate("AnySceneConverter"); + converter->configuration().setValue("noSuchOption", "isHere"); + + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter->convertToFile(mesh, Utility::Directory::join(ANYSCENECONVERTER_TEST_OUTPUT_DIR, "file.ply"))); + CORRADE_COMPARE(out.str(), "Trade::AnySceneConverter::convertToFile(): option noSuchOption not recognized by StanfordSceneConverter\n"); +} + }}}} CORRADE_TEST_MAIN(Magnum::Trade::Test::AnySceneConverterTest) diff --git a/src/MagnumPlugins/AnySceneConverter/Test/CMakeLists.txt b/src/MagnumPlugins/AnySceneConverter/Test/CMakeLists.txt index 2672a88ca..38f58507c 100644 --- a/src/MagnumPlugins/AnySceneConverter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/AnySceneConverter/Test/CMakeLists.txt @@ -26,9 +26,11 @@ if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) set(ANYSCENECONVERTER_TEST_OUTPUT_DIR "write") set(PLY_FILE triangle.ply) + set(PLY_OBJECTID_FILE objectid.ply) else() set(ANYSCENECONVERTER_TEST_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(PLY_FILE ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/AnySceneImporter/Test/triangle.ply) + set(PLY_OBJECTID_FILE ${CMAKE_CURRENT_SOURCE_DIR}/objectid.ply) endif() # CMake before 3.8 has broken $ expressions for iOS (see @@ -51,7 +53,8 @@ file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h corrade_add_test(AnySceneConverterTest AnySceneConverterTest.cpp LIBRARIES MagnumTrade FILES - ../../AnySceneImporter/Test/triangle.ply) + ../../AnySceneImporter/Test/triangle.ply + objectid.ply) target_include_directories(AnySceneConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) if(MAGNUM_ANYSCENECONVERTER_BUILD_STATIC) target_link_libraries(AnySceneConverterTest PRIVATE AnySceneConverter) diff --git a/src/MagnumPlugins/AnySceneConverter/Test/configure.h.cmake b/src/MagnumPlugins/AnySceneConverter/Test/configure.h.cmake index 2d8e8e8de..0fa2a7cae 100644 --- a/src/MagnumPlugins/AnySceneConverter/Test/configure.h.cmake +++ b/src/MagnumPlugins/AnySceneConverter/Test/configure.h.cmake @@ -26,6 +26,7 @@ #cmakedefine ANYSCENECONVERTER_PLUGIN_FILENAME "${ANYSCENECONVERTER_PLUGIN_FILENAME}" #define ANYSCENECONVERTER_TEST_OUTPUT_DIR "${ANYSCENECONVERTER_TEST_OUTPUT_DIR}" #define PLY_FILE "${PLY_FILE}" +#define PLY_OBJECTID_FILE "${PLY_OBJECTID_FILE}" #ifdef CORRADE_TARGET_WINDOWS #ifdef CORRADE_IS_DEBUG_BUILD diff --git a/src/MagnumPlugins/AnySceneConverter/Test/objectid.ply b/src/MagnumPlugins/AnySceneConverter/Test/objectid.ply new file mode 100644 index 0000000000000000000000000000000000000000..525034f3a5e9e439c72933683dbce8d6f68143c3 GIT binary patch literal 249 zcmZ8bK?=e!5L^{J1W&$TUmzAA;6(&aJ|V<(Lj!3V(o`B><4^pWYqb`77?>GmXD7HU zOcQIB@MgV=Svs4NBT2rr%44zH3vyJGPk5)8sK=g09!;&_U9uuoQWOC2`T@>cW&p=gKR1IOJ%Ba9>Z2Bn GCF2dBen`#$ literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp b/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp index 00e78ff2c..12d60b569 100644 --- a/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp +++ b/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.cpp @@ -43,6 +43,7 @@ #include "Magnum/Trade/SceneData.h" #include "Magnum/Trade/SkinData.h" #include "Magnum/Trade/TextureData.h" +#include "MagnumPlugins/Implementation/propagateConfiguration.h" #ifdef MAGNUM_BUILD_DEPRECATED #define _MAGNUM_NO_DEPRECATED_MESHDATA /* So it doesn't yell here */ @@ -140,11 +141,12 @@ void AnySceneImporter::doOpenFile(const std::string& filename) { Error{} << "Trade::AnySceneImporter::openFile(): cannot load the" << plugin << "plugin"; return; } + + const PluginManager::PluginMetadata* const metadata = manager()->metadata(plugin); + CORRADE_INTERNAL_ASSERT(metadata); if(flags() & ImporterFlag::Verbose) { Debug d; d << "Trade::AnySceneImporter::openFile(): using" << plugin; - PluginManager::PluginMetadata* metadata = manager()->metadata(plugin); - CORRADE_INTERNAL_ASSERT(metadata); if(plugin != metadata->name()) d << "(provided by" << metadata->name() << Debug::nospace << ")"; } @@ -153,6 +155,9 @@ void AnySceneImporter::doOpenFile(const std::string& filename) { Containers::Pointer importer = static_cast*>(manager())->instantiate(plugin); importer->setFlags(flags()); + /* Propagate configuration */ + Magnum::Implementation::propagateConfiguration("Trade::AnySceneImporter::openFile():", {}, metadata->name(), configuration(), importer->configuration()); + /* Try to open the file (error output should be printed by the plugin itself) */ if(!importer->openFile(filename)) return; diff --git a/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.h b/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.h index 312252b75..836d504d6 100644 --- a/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.h +++ b/src/MagnumPlugins/AnySceneImporter/AnySceneImporter.h @@ -127,6 +127,21 @@ target_link_libraries(your-app PRIVATE Magnum::AnySceneImporter) See @ref building, @ref cmake, @ref plugins and @ref file-formats for more information. + +@section Trade-AnySceneImporter-proxy Interface proxying and option propagation + +On a call to @ref openFile(), a file format is detected from the extension and +a corresponding plugin is loaded. After that, flags set via @ref setFlags() and +options set through @ref configuration() are propagated to the concrete +implementation, with a warning emitted in case given option is not present in +the default configuration of the target plugin. + +Calls to the @ref animation(), @ref scene(), @ref light(), @ref camera(), +@ref object2D(), @ref object3D(), @ref skin2D(), @ref skin3D(), @ref mesh(), +@ref material(), @ref texture(), @ref image1D(), @ref image2D(), @ref image3D() +and corresponding count-/name-related functions are then proxied to the +concrete implementation. The @ref close() function closes and discards the +internally instantiated plugin; @ref isOpened() works as usual. */ class MAGNUM_ANYSCENEIMPORTER_EXPORT AnySceneImporter: public AbstractImporter { public: diff --git a/src/MagnumPlugins/AnySceneImporter/Test/AnySceneImporterTest.cpp b/src/MagnumPlugins/AnySceneImporter/Test/AnySceneImporterTest.cpp index 62c1898aa..888cc45fc 100644 --- a/src/MagnumPlugins/AnySceneImporter/Test/AnySceneImporterTest.cpp +++ b/src/MagnumPlugins/AnySceneImporter/Test/AnySceneImporterTest.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,8 @@ struct AnySceneImporterTest: TestSuite::Tester { void unknown(); void propagateFlags(); + void propagateConfiguration(); + void propagateConfigurationUnknown(); /* Explicitly forbid system-wide plugin dependencies */ PluginManager::Manager _manager{"nonexistent"}; @@ -98,7 +101,9 @@ AnySceneImporterTest::AnySceneImporterTest() { addTests({&AnySceneImporterTest::unknown, - &AnySceneImporterTest::propagateFlags}); + &AnySceneImporterTest::propagateFlags, + &AnySceneImporterTest::propagateConfiguration, + &AnySceneImporterTest::propagateConfigurationUnknown}); /* Load the plugin directly from the build tree. Otherwise it's static and already loaded. */ @@ -209,6 +214,63 @@ void AnySceneImporterTest::propagateFlags() { CORRADE_COMPARE(out.str().substr(0, expected.size()), expected); } +void AnySceneImporterTest::propagateConfiguration() { + PluginManager::Manager manager{MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR}; + #ifdef ANYSCENEIMPORTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSCENEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("AssimpImporter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("AssimpImporter plugin can't be loaded."); + /* Ensure Assimp is used for PLY files and not our StanfordImporter */ + manager.setPreferredPlugins("StanfordImporter", {"AssimpImporter"}); + + Containers::Pointer importer = manager.instantiate("AnySceneImporter"); + + { + CORRADE_VERIFY(importer->openFile(PLY_FILE)); + + Containers::Optional mesh = importer->mesh(0); + CORRADE_VERIFY(mesh); + CORRADE_VERIFY(!mesh->hasAttribute(Trade::MeshAttribute::Normal)); + } { + importer->configuration().addGroup("postprocess")->setValue("GenNormals", true); + CORRADE_VERIFY(importer->openFile(PLY_FILE)); + + Containers::Optional mesh = importer->mesh(0); + CORRADE_VERIFY(mesh); + CORRADE_VERIFY(mesh->hasAttribute(Trade::MeshAttribute::Normal)); + } +} + +void AnySceneImporterTest::propagateConfigurationUnknown() { + PluginManager::Manager manager{MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR}; + #ifdef ANYSCENEIMPORTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSCENEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("AssimpImporter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("AssimpImporter plugin can't be loaded."); + /* Ensure Assimp is used for PLY files and not our StanfordImporter. This + thus also accidentally checks that correct plugin name (and not the + alias) is used in the warning messages. */ + manager.setPreferredPlugins("StanfordImporter", {"AssimpImporter"}); + + Containers::Pointer importer = manager.instantiate("AnySceneImporter"); + importer->configuration().setValue("noSuchOption", "isHere"); + importer->configuration().addGroup("postprocess"); + importer->configuration().group("postprocess")->setValue("notHere", false); + importer->configuration().group("postprocess")->addGroup("feh")->setValue("noHereNotEither", false); + + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(importer->openFile(PLY_FILE)); + CORRADE_COMPARE(out.str(), + "Trade::AnySceneImporter::openFile(): option noSuchOption not recognized by AssimpImporter\n" + "Trade::AnySceneImporter::openFile(): option postprocess/notHere not recognized by AssimpImporter\n" + "Trade::AnySceneImporter::openFile(): option postprocess/feh/noHereNotEither not recognized by AssimpImporter\n"); +} + }}}} CORRADE_TEST_MAIN(Magnum::Trade::Test::AnySceneImporterTest) diff --git a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp index 130527699..c5c7888db 100644 --- a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp +++ b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp @@ -35,6 +35,8 @@ #include #include +#include "MagnumPlugins/Implementation/propagateConfiguration.h" + namespace Magnum { namespace ShaderTools { struct AnyConverter::State { @@ -223,6 +225,9 @@ std::pair AnyConverter::doValidateFile(const Stage sta if(!_state->definitionViews.empty()) converter->setDefinitions(_state->definitionViews); + /* Propagate configuration */ + Magnum::Implementation::propagateConfiguration("ShaderTools::AnyConverter::validateFile():", {}, metadata->name(), configuration(), converter->configuration()); + /* Try to validate the file (error output should be printed by the plugin itself) */ return converter->validateFile(stage, filename); @@ -277,6 +282,9 @@ std::pair AnyConverter::doValidateData(const Stage sta if(!_state->definitionViews.empty()) converter->setDefinitions(_state->definitionViews); + /* Propagate configuration */ + Magnum::Implementation::propagateConfiguration("ShaderTools::AnyConverter::validateData():", {}, metadata->name(), configuration(), converter->configuration()); + /* Try to validate the data (error output should be printed by the plugin itself) */ return converter->validateData(stage, data); @@ -358,6 +366,9 @@ bool AnyConverter::doConvertFileToFile(const Stage stage, const Containers::Stri if(!_state->optimizationLevel.isEmpty()) converter->setOptimizationLevel(_state->optimizationLevel); + /* Propagate configuration */ + Magnum::Implementation::propagateConfiguration("ShaderTools::AnyConverter::convertFileToFile():", {}, metadata->name(), configuration(), converter->configuration()); + /* Try to convert the file (error output should be printed by the plugin itself) */ return converter->convertFileToFile(stage, from, to); @@ -440,6 +451,9 @@ Containers::Array AnyConverter::doConvertFileToData(const Stage stage, con if(!_state->optimizationLevel.isEmpty()) converter->setOptimizationLevel(_state->optimizationLevel); + /* Propagate configuration */ + Magnum::Implementation::propagateConfiguration("ShaderTools::AnyConverter::convertFileToData():", {}, metadata->name(), configuration(), converter->configuration()); + /* Try to convert the file (error output should be printed by the plugin itself) */ return converter->convertFileToData(stage, filename); @@ -520,6 +534,9 @@ Containers::Array AnyConverter::doConvertDataToData(const Stage stage, con if(!_state->optimizationLevel.isEmpty()) converter->setOptimizationLevel(_state->optimizationLevel); + /* Propagate configuration */ + Magnum::Implementation::propagateConfiguration("ShaderTools::AnyConverter::convertDataToData():", {}, metadata->name(), configuration(), converter->configuration()); + /* Try to convert the file (error output should be printed by the plugin itself) */ return converter->convertDataToData(stage, from); diff --git a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.h b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.h index f1529dd53..a8c4dcc5d 100644 --- a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.h +++ b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.h @@ -121,6 +121,24 @@ target_link_libraries(your-app PRIVATE Magnum::AnyShaderConverter) @endcode See @ref building, @ref cmake and @ref plugins for more information. + +@section ShaderTools-AnyConverter-proxy Interface proxying and option propagation + +On a call to @ref validateFile() / @ref validateData(), @ref convertFileToFile() +/ @ref convertFileToData() / @ref convertDataToData(), an input/output file +format is detected from either the extensions or taken from the +@ref setInputFormat() and @ref setOutputFormat() calls and a corresponding +plugin is loaded. After that, everything set via @ref setFlags(), +@ref setInputFormat(), @ref setOutputFormat(), @ref setDefinitions(), +@ref setDebugInfoLevel(), @ref setOptimizationLevel() is propagated to the +concrete implementation, with an error emitted in case the target plugin +doesn't support given feature. Options set through @ref configuration() are +propagated as well, with just a warning emitted in case given option is not +present in the default configuration of the target plugin. + +The output of the @ref validateFile() / @ref validateData(), +@ref convertFileToFile() / @ref convertFileToData() / @ref convertDataToData() +function called on the concrete implementation is then proxied back. */ class MAGNUM_ANYSHADERCONVERTER_EXPORT AnyConverter: public AbstractConverter { public: diff --git a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp index 08e862f5d..dc4c46317 100644 --- a/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp +++ b/src/MagnumPlugins/AnyShaderConverter/Test/AnyConverterTest.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,8 @@ struct AnyConverterTest: TestSuite::Tester { void validateFilePropagateInputVersion(); void validateFilePropagateOutputVersion(); void validateFilePropagatePreprocess(); + void validateFilePropagateConfiguration(); + void validateFilePropagateConfigurationUnknown(); void validateData(); void validateDataPluginLoadFailed(); @@ -62,6 +65,8 @@ struct AnyConverterTest: TestSuite::Tester { void validateDataPropagateInputVersion(); void validateDataPropagateOutputVersion(); void validateDataPropagatePreprocess(); + void validateDataPropagateConfiguration(); + void validateDataPropagateConfigurationUnknown(); void convertFileToFile(); void convertFileToFilePluginLoadFailed(); @@ -77,6 +82,8 @@ struct AnyConverterTest: TestSuite::Tester { void convertFileToFilePropagatePreprocess(); void convertFileToFilePropagateDebugInfo(); void convertFileToFilePropagateOptimization(); + void convertFileToFilePropagateConfiguration(); + void convertFileToFilePropagateConfigurationUnknown(); void convertFileToData(); void convertFileToDataPluginLoadFailed(); @@ -92,6 +99,8 @@ struct AnyConverterTest: TestSuite::Tester { void convertFileToDataPropagatePreprocess(); void convertFileToDataPropagateDebugInfo(); void convertFileToDataPropagateOptimization(); + void convertFileToDataPropagateConfiguration(); + void convertFileToDataPropagateConfigurationUnknown(); void convertDataToData(); void convertDataToDataPluginLoadFailed(); @@ -107,6 +116,8 @@ struct AnyConverterTest: TestSuite::Tester { void convertDataToDataPropagatePreprocess(); void convertDataToDataPropagateDebugInfo(); void convertDataToDataPropagateOptimization(); + void convertDataToDataPropagateConfiguration(); + void convertDataToDataPropagateConfigurationUnknown(); void detectValidate(); void detectValidateExplicitFormat(); @@ -151,6 +162,8 @@ AnyConverterTest::AnyConverterTest() { &AnyConverterTest::validateFilePropagateInputVersion, &AnyConverterTest::validateFilePropagateOutputVersion, &AnyConverterTest::validateFilePropagatePreprocess, + &AnyConverterTest::validateFilePropagateConfiguration, + &AnyConverterTest::validateFilePropagateConfigurationUnknown, &AnyConverterTest::validateData, &AnyConverterTest::validateDataPluginLoadFailed, @@ -161,6 +174,8 @@ AnyConverterTest::AnyConverterTest() { &AnyConverterTest::validateDataPropagateInputVersion, &AnyConverterTest::validateDataPropagateOutputVersion, &AnyConverterTest::validateDataPropagatePreprocess, + &AnyConverterTest::validateDataPropagateConfiguration, + &AnyConverterTest::validateDataPropagateConfigurationUnknown, &AnyConverterTest::convertFileToFile, &AnyConverterTest::convertFileToFilePluginLoadFailed, @@ -176,6 +191,8 @@ AnyConverterTest::AnyConverterTest() { &AnyConverterTest::convertFileToFilePropagatePreprocess, &AnyConverterTest::convertFileToFilePropagateDebugInfo, &AnyConverterTest::convertFileToFilePropagateOptimization, + &AnyConverterTest::convertFileToFilePropagateConfiguration, + &AnyConverterTest::convertFileToFilePropagateConfigurationUnknown, &AnyConverterTest::convertFileToData, &AnyConverterTest::convertFileToDataPluginLoadFailed, @@ -191,6 +208,8 @@ AnyConverterTest::AnyConverterTest() { &AnyConverterTest::convertFileToDataPropagatePreprocess, &AnyConverterTest::convertFileToDataPropagateDebugInfo, &AnyConverterTest::convertFileToDataPropagateOptimization, + &AnyConverterTest::convertFileToDataPropagateConfiguration, + &AnyConverterTest::convertFileToDataPropagateConfigurationUnknown, &AnyConverterTest::convertDataToData, &AnyConverterTest::convertDataToDataPluginLoadFailed, @@ -205,7 +224,9 @@ AnyConverterTest::AnyConverterTest() { &AnyConverterTest::convertDataToDataPropagateOutputVersion, &AnyConverterTest::convertDataToDataPropagatePreprocess, &AnyConverterTest::convertDataToDataPropagateDebugInfo, - &AnyConverterTest::convertDataToDataPropagateOptimization}); + &AnyConverterTest::convertDataToDataPropagateOptimization, + &AnyConverterTest::convertDataToDataPropagateConfiguration, + &AnyConverterTest::convertDataToDataPropagateConfigurationUnknown}); addInstancedTests({&AnyConverterTest::detectValidate}, Containers::arraySize(DetectValidateData)); @@ -398,6 +419,53 @@ void AnyConverterTest::validateFilePropagatePreprocess() { std::make_pair(true, Utility::formatString("WARNING: {}:10: 'different__but_also_wrong' : identifiers containing consecutive underscores (\"__\") are reserved", filename))); } +void AnyConverterTest::validateFilePropagateConfiguration() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + const std::string filename = Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "version-not-first.glsl"); + + { + CORRADE_COMPARE(converter->validateFile(Stage::Fragment, filename), + std::make_pair(false, Utility::formatString("ERROR: {}:2: '#version' : must occur first in shader \nERROR: 1 compilation errors. No code generated.", filename))); + } { + converter->configuration().setValue("permissive", true); + /* Lol stupid thing, apparently it has two differently worded messages + for the same thing? Dumpster fire. */ + CORRADE_COMPARE(converter->validateFile(Stage::Fragment, filename), + std::make_pair(true, "WARNING: 0:0: '#version' : Illegal to have non-comment, non-whitespace tokens before #version")); + } +} + +void AnyConverterTest::validateFilePropagateConfigurationUnknown() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + converter->configuration().setValue("noSuchOption", "isHere"); + /* So it doesn't warn about anything */ + converter->setDefinitions({{"reserved__identifier", "sorry"}}); + + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_COMPARE(converter->validateFile(Stage::Fragment, Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl")), + std::make_pair(true, "")); + CORRADE_COMPARE(out.str(), + "ShaderTools::AnyConverter::validateFile(): option noSuchOption not recognized by GlslangShaderConverter\n"); +} + void AnyConverterTest::validateData() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME @@ -577,6 +645,55 @@ void AnyConverterTest::validateDataPropagatePreprocess() { std::make_pair(true, "WARNING: 0:10: 'different__but_also_wrong' : identifiers containing consecutive underscores (\"__\") are reserved")); } +void AnyConverterTest::validateDataPropagateConfiguration() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + converter->setInputFormat(Format::Glsl); + + const std::string filename = Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "version-not-first.glsl"); + + { + CORRADE_COMPARE(converter->validateData(Stage::Fragment, Utility::Directory::read(filename)), + std::make_pair(false, "ERROR: 0:2: '#version' : must occur first in shader \nERROR: 1 compilation errors. No code generated.")); + } { + converter->configuration().setValue("permissive", true); + /* Lol stupid thing, apparently it has two differently worded messages + for the same thing? Dumpster fire. */ + CORRADE_COMPARE(converter->validateData(Stage::Fragment, Utility::Directory::read(filename)), + std::make_pair(true, Utility::formatString("WARNING: 0:0: '#version' : Illegal to have non-comment, non-whitespace tokens before #version"))); + } +} + +void AnyConverterTest::validateDataPropagateConfigurationUnknown() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + converter->setInputFormat(Format::Glsl); + converter->configuration().setValue("noSuchOption", "isHere"); + /* So it doesn't warn about anything */ + converter->setDefinitions({{"reserved__identifier", "sorry"}}); + + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_COMPARE(converter->validateData(Stage::Fragment, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl"))), + std::make_pair(true, "")); + CORRADE_COMPARE(out.str(), + "ShaderTools::AnyConverter::validateData(): option noSuchOption not recognized by GlslangShaderConverter\n"); +} + void AnyConverterTest::convertFileToFile() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME @@ -880,6 +997,59 @@ void AnyConverterTest::convertFileToFilePropagateOptimization() { "ShaderTools::SpirvToolsConverter::convertDataToData(): optimization level should be 0, 1, s, legalizeHlsl or empty but got 2\n"); } +void AnyConverterTest::convertFileToFilePropagateConfiguration() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + const std::string input = Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "version-not-first.glsl"); + const std::string output = Utility::Directory::join(ANYSHADERCONVERTER_TEST_OUTPUT_DIR, "file.spv"); + + { + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToFile(Stage::Fragment, input, output)); + CORRADE_COMPARE(out.str(), + Utility::formatString("ShaderTools::GlslangConverter::convertDataToData(): compilation failed:\nERROR: {}:2: '#version' : must occur first in shader \nERROR: 1 compilation errors. No code generated.\n", input)); + } { + converter->configuration().setValue("permissive", true); + /* Lol stupid thing, apparently it has two differently worded messages + for the same thing? Dumpster fire. */ + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter->convertFileToFile(Stage::Fragment, input, output)); + CORRADE_COMPARE(out.str(), + "ShaderTools::GlslangConverter::convertDataToData(): compilation succeeded with the following message:\nWARNING: 0:0: '#version' : Illegal to have non-comment, non-whitespace tokens before #version\n"); + } +} + +void AnyConverterTest::convertFileToFilePropagateConfigurationUnknown() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + converter->configuration().setValue("noSuchOption", "isHere"); + /* So it doesn't warn about anything */ + converter->setDefinitions({{"reserved__identifier", "sorry"}}); + + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter->convertFileToFile(Stage::Fragment, Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl"), Utility::Directory::join(ANYSHADERCONVERTER_TEST_OUTPUT_DIR, "file.glsl"))); + CORRADE_COMPARE(out.str(), + "ShaderTools::AnyConverter::convertFileToFile(): option noSuchOption not recognized by GlslangShaderConverter\n"); +} + void AnyConverterTest::convertFileToData() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME @@ -1191,6 +1361,61 @@ void AnyConverterTest::convertFileToDataPropagateOptimization() { "ShaderTools::SpirvToolsConverter::convertDataToData(): optimization level should be 0, 1, s, legalizeHlsl or empty but got 2\n"); } +void AnyConverterTest::convertFileToDataPropagateConfiguration() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setOutputFormat(Format::Spirv); + + const std::string input = Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "version-not-first.glsl"); + + { + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertFileToData(Stage::Fragment, input)); + CORRADE_COMPARE(out.str(), + Utility::formatString("ShaderTools::GlslangConverter::convertDataToData(): compilation failed:\nERROR: {}:2: '#version' : must occur first in shader \nERROR: 1 compilation errors. No code generated.\n", input)); + } { + converter->configuration().setValue("permissive", true); + /* Lol stupid thing, apparently it has two differently worded messages + for the same thing? Dumpster fire. */ + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter->convertFileToData(Stage::Fragment, input)); + CORRADE_COMPARE(out.str(), + "ShaderTools::GlslangConverter::convertDataToData(): compilation succeeded with the following message:\nWARNING: 0:0: '#version' : Illegal to have non-comment, non-whitespace tokens before #version\n"); + } +} + +void AnyConverterTest::convertFileToDataPropagateConfigurationUnknown() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + converter->setOutputFormat(Format::Spirv); + converter->configuration().setValue("noSuchOption", "isHere"); + /* So it doesn't warn about anything */ + converter->setDefinitions({{"reserved__identifier", "sorry"}}); + + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter->convertFileToData(Stage::Fragment, Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl"))); + CORRADE_COMPARE(out.str(), + "ShaderTools::AnyConverter::convertFileToData(): option noSuchOption not recognized by GlslangShaderConverter\n"); +} + void AnyConverterTest::convertDataToData() { PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME @@ -1509,6 +1734,63 @@ void AnyConverterTest::convertDataToDataPropagateOptimization() { "ShaderTools::SpirvToolsConverter::convertDataToData(): optimization level should be 0, 1, s, legalizeHlsl or empty but got 2\n"); } +void AnyConverterTest::convertDataToDataPropagateConfiguration() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + + converter->setInputFormat(Format::Glsl); + converter->setOutputFormat(Format::Spirv); + + const std::string input = Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "version-not-first.glsl"); + + { + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertDataToData(Stage::Fragment, Utility::Directory::read(input))); + CORRADE_COMPARE(out.str(), + "ShaderTools::GlslangConverter::convertDataToData(): compilation failed:\nERROR: 0:2: '#version' : must occur first in shader \nERROR: 1 compilation errors. No code generated.\n"); + } { + converter->configuration().setValue("permissive", true); + /* Lol stupid thing, apparently it has two differently worded messages + for the same thing? Dumpster fire. */ + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter->convertDataToData(Stage::Fragment, Utility::Directory::read(input))); + CORRADE_COMPARE(out.str(), + "ShaderTools::GlslangConverter::convertDataToData(): compilation succeeded with the following message:\nWARNING: 0:0: '#version' : Illegal to have non-comment, non-whitespace tokens before #version\n"); + } +} + +void AnyConverterTest::convertDataToDataPropagateConfigurationUnknown() { + PluginManager::Manager manager{MAGNUM_PLUGINS_SHADERCONVERTER_INSTALL_DIR}; + #ifdef ANYSHADERCONVERTER_PLUGIN_FILENAME + CORRADE_VERIFY(manager.load(ANYSHADERCONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif + + if(manager.load("GlslangShaderConverter") < PluginManager::LoadState::Loaded) + CORRADE_SKIP("GlslangShaderConverter plugin can't be loaded."); + + Containers::Pointer converter = manager.instantiate("AnyShaderConverter"); + converter->setInputFormat(Format::Glsl); + converter->setOutputFormat(Format::Spirv); + converter->configuration().setValue("noSuchOption", "isHere"); + /* So it doesn't warn about anything */ + converter->setDefinitions({{"reserved__identifier", "sorry"}}); + + std::ostringstream out; + Warning redirectWarning{&out}; + CORRADE_VERIFY(converter->convertDataToData(Stage::Fragment, Utility::Directory::read(Utility::Directory::join(ANYSHADERCONVERTER_TEST_DIR, "file.glsl")))); + CORRADE_COMPARE(out.str(), + "ShaderTools::AnyConverter::convertDataToData(): option noSuchOption not recognized by GlslangShaderConverter\n"); +} + void AnyConverterTest::detectValidate() { auto&& data = DetectValidateData[testCaseInstanceId()]; setTestCaseDescription(data.name); diff --git a/src/MagnumPlugins/AnyShaderConverter/Test/CMakeLists.txt b/src/MagnumPlugins/AnyShaderConverter/Test/CMakeLists.txt index 2c91fe1f5..87fa08ce5 100644 --- a/src/MagnumPlugins/AnyShaderConverter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/AnyShaderConverter/Test/CMakeLists.txt @@ -49,7 +49,8 @@ corrade_add_test(AnyShaderConverterTest AnyConverterTest.cpp LIBRARIES MagnumShaderTools FILES file.glsl - file.spv) + file.spv + version-not-first.glsl) target_include_directories(AnyShaderConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) if(MAGNUM_ANYSHADERCONVERTER_BUILD_STATIC) target_link_libraries(AnyShaderConverterTest PRIVATE AnyShaderConverter) diff --git a/src/MagnumPlugins/AnyShaderConverter/Test/version-not-first.glsl b/src/MagnumPlugins/AnyShaderConverter/Test/version-not-first.glsl new file mode 100644 index 000000000..15197432d --- /dev/null +++ b/src/MagnumPlugins/AnyShaderConverter/Test/version-not-first.glsl @@ -0,0 +1,4 @@ +#define a haha +#version 450 + +void main() {} diff --git a/src/MagnumPlugins/Implementation/propagateConfiguration.h b/src/MagnumPlugins/Implementation/propagateConfiguration.h new file mode 100644 index 000000000..0bf903f17 --- /dev/null +++ b/src/MagnumPlugins/Implementation/propagateConfiguration.h @@ -0,0 +1,69 @@ +#ifndef Magnum_Implementation_propagateConfiguration_h +#define Magnum_Implementation_propagateConfiguration_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include + +#include "Magnum/Magnum.h" + +/* Used by Any* plugins to propagate configuration to the concrete + implementation. Assumes that the Any* plugin itself doesn't have any + configuration options and so propagates all groups and values that were + set, emitting a warning if the target doesn't have such option in its + default configuration. */ + +namespace Magnum { namespace Implementation { + +/* Used only in plugins where we don't want it to be exported */ +namespace { + +void propagateConfiguration(const char* warningPrefix, const Containers::String& groupPrefix, const Containers::StringView plugin, const Utility::ConfigurationGroup& src, Utility::ConfigurationGroup& dst) { + using namespace Containers::Literals; + + /* Propagate values */ + for(Containers::Pair value: src.values()) { + if(!dst.hasValue(value.first())) { + Warning{} << warningPrefix << "option" << "/"_s.joinWithoutEmptyParts({groupPrefix, value.first()}) << "not recognized by" << plugin; + } + + dst.setValue(value.first(), value.second()); + } + + /* Recursively propagate groups */ + for(Containers::Pair> group: src.groups()) { + Utility::ConfigurationGroup* dstGroup = dst.group(group.first()); + if(!dstGroup) dstGroup = dst.addGroup(group.first()); + propagateConfiguration(warningPrefix, "/"_s.joinWithoutEmptyParts({groupPrefix, group.first()}), plugin, group.second(), *dstGroup); + } +} + +} + +}} + +#endif From 99395eb69a086d5a52e933936f1568e9c209df9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 24 Jun 2021 15:29:27 +0200 Subject: [PATCH 88/93] Doc++ --- doc/file-formats.dox | 18 +++++++++--------- .../AnyImageImporter/AnyImageImporter.h | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/file-formats.dox b/doc/file-formats.dox index 08d77c8d1..4548a40c9 100644 --- a/doc/file-formats.dox +++ b/doc/file-formats.dox @@ -30,15 +30,15 @@ namespace Magnum { @tableofcontents @m_footernavigation -The @ref Trade::AnyImageImporter "AnyImageImporter", -@ref Trade::AnySceneImporter "AnySceneImporter", -@ref Trade::AnyImageConverter "AnyImageConverter", -@ref Trade::AnySceneConverter "AnySceneConverter" and other `Any*` plugins can -be used for generic handling of any of the recognized formats, they'll proxy -the operation to a concrete plugin implementation. The following tables list -the most widely used formats with alternative plugin implementations and known -caveats for each. When one format is supported by more than one plugin, you -can use @ref Corrade::PluginManager::AbstractManager::setPreferredPlugins() to +The @ref Audio::AnyImporter "AnyAudioImporter", +@relativeref{Trade,AnyImageImporter}, @relativeref{Trade,AnySceneImporter}, +@relativeref{Trade,AnyImageConverter}, @relativeref{Trade,AnySceneConverter} +and @ref ShaderTools::AnyConverter "AnyShaderConverter" plugins can be used for +generic handling of any of the recognized formats, they'll proxy the operation +to a concrete plugin implementation. The following tables list the most widely +used formats with alternative plugin implementations and known caveats for +each. When one format is supported by more than one plugin, you can use +@ref Corrade::PluginManager::AbstractManager::setPreferredPlugins() to prioritize a particular plugin implementation. See the @ref file-formats-legend section at the bottom of the page for a diff --git a/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.h b/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.h index 730adffca..01637d825 100644 --- a/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.h +++ b/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.h @@ -56,8 +56,8 @@ namespace Magnum { namespace Trade { Detects file type based on file extension, loads corresponding plugin and then tries to open the file with it. Supported formats: -- Basis Universal (`*.basis`), loaded @ref BasisImporter or any other plugin - that provides it +- Basis Universal (`*.basis` or data with corresponding signature), loaded + with @ref BasisImporter or any other plugin that provides it - Windows Bitmap (`*.bmp`), loaded with any plugin that provides `BmpImporter` - DirectDraw Surface (`*.dds` or data with corresponding signature), loaded with @ref DdsImporter or any other plugin that provides it From 6087107e0d5c6f20648c54ada0b7fab731da720f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 24 Jun 2021 17:53:29 +0200 Subject: [PATCH 89/93] Trade: don't use deprecated APIs in deprecated APIs. Because MSVC IS STUPID and emits a ton of warnings here. All sane compilers are sane and silent. It doesn't fucking make sense to warn here, COME ON. --- src/Magnum/Trade/PhongMaterialData.cpp | 32 +++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Magnum/Trade/PhongMaterialData.cpp b/src/Magnum/Trade/PhongMaterialData.cpp index 9ef0ccf7e..5672ae2e8 100644 --- a/src/Magnum/Trade/PhongMaterialData.cpp +++ b/src/Magnum/Trade/PhongMaterialData.cpp @@ -44,13 +44,13 @@ PhongMaterialData::PhongMaterialData(const Flags flags, const Color4& ambientCol Containers::Array data; if(flags & Flag::DoubleSided) - arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::DoubleSided, true); + arrayAppend(data, InPlaceInit, MaterialAttribute::DoubleSided, true); if(alphaMode == MaterialAlphaMode::Blend) - arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::AlphaBlend, true); + arrayAppend(data, InPlaceInit, MaterialAttribute::AlphaBlend, true); /* Include a mask also if it has a non-default value to stay compatible with existing behavior */ if(alphaMode == MaterialAlphaMode::Mask || alphaMask != 0.0f) - arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::AlphaMask, alphaMask); + arrayAppend(data, InPlaceInit, MaterialAttribute::AlphaMask, alphaMask); CORRADE_ASSERT(!(flags & Flag::TextureTransformation) || (flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture)), "Trade::PhongMaterialData: texture transformation enabled but the material has no textures", data); @@ -59,37 +59,37 @@ PhongMaterialData::PhongMaterialData(const Flags flags, const Color4& ambientCol CORRADE_ASSERT((flags & Flag::TextureCoordinateSets) || (ambientTextureCoordinates == 0 && diffuseTextureCoordinates == 0 && specularTextureCoordinates == 0 && normalTextureCoordinates == 0), "PhongMaterialData::PhongMaterialData: non-zero texture coordinate sets require Flag::TextureCoordinates to be enabled", data); - arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::AmbientColor, ambientColor); + arrayAppend(data, InPlaceInit, MaterialAttribute::AmbientColor, ambientColor); if(flags & Flag::AmbientTexture) { - arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::AmbientTexture, ambientTexture); + arrayAppend(data, InPlaceInit, MaterialAttribute::AmbientTexture, ambientTexture); if(ambientTextureCoordinates) - arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::AmbientTextureCoordinates, ambientTextureCoordinates); + arrayAppend(data, InPlaceInit, MaterialAttribute::AmbientTextureCoordinates, ambientTextureCoordinates); } - arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::DiffuseColor, diffuseColor); + arrayAppend(data, InPlaceInit, MaterialAttribute::DiffuseColor, diffuseColor); if(flags & Flag::DiffuseTexture) { - arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::DiffuseTexture, diffuseTexture); + arrayAppend(data, InPlaceInit, MaterialAttribute::DiffuseTexture, diffuseTexture); if(diffuseTextureCoordinates) - arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::DiffuseTextureCoordinates, diffuseTextureCoordinates); + arrayAppend(data, InPlaceInit, MaterialAttribute::DiffuseTextureCoordinates, diffuseTextureCoordinates); } - arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::SpecularColor, specularColor); + arrayAppend(data, InPlaceInit, MaterialAttribute::SpecularColor, specularColor); if(flags & Flag::SpecularTexture) { - arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::SpecularTexture, specularTexture); + arrayAppend(data, InPlaceInit, MaterialAttribute::SpecularTexture, specularTexture); if(specularTextureCoordinates) - arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::SpecularTextureCoordinates, specularTextureCoordinates); + arrayAppend(data, InPlaceInit, MaterialAttribute::SpecularTextureCoordinates, specularTextureCoordinates); } if(flags & Flag::NormalTexture) { - arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::NormalTexture, normalTexture); + arrayAppend(data, InPlaceInit, MaterialAttribute::NormalTexture, normalTexture); if(normalTextureCoordinates) - arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::NormalTextureCoordinates, normalTextureCoordinates); + arrayAppend(data, InPlaceInit, MaterialAttribute::NormalTextureCoordinates, normalTextureCoordinates); } if(flags & Flag::TextureTransformation) - arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::TextureMatrix, textureMatrix); + arrayAppend(data, InPlaceInit, MaterialAttribute::TextureMatrix, textureMatrix); - arrayAppend(data, Containers::InPlaceInit, MaterialAttribute::Shininess, shininess); + arrayAppend(data, InPlaceInit, MaterialAttribute::Shininess, shininess); /* Convert back to a non-growable Array as importers don't allow custom deleters in plugin implementations */ From 416704fa47822d6532bde0d0ee54c8e4988030aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 25 Jun 2021 18:57:53 +0200 Subject: [PATCH 90/93] Any{Image,Scene,Shader}Converter: modernize the string code a bit. Only those use StringViews in the plugin API so far, importers not yet. --- .../AnyImageConverter/AnyImageConverter.cpp | 51 +++++++------- .../AnySceneConverter/AnySceneConverter.cpp | 17 +++-- .../AnyShaderConverter/AnyConverter.cpp | 70 +++++++++---------- 3 files changed, 72 insertions(+), 66 deletions(-) diff --git a/src/MagnumPlugins/AnyImageConverter/AnyImageConverter.cpp b/src/MagnumPlugins/AnyImageConverter/AnyImageConverter.cpp index 5ea4570f4..862086131 100644 --- a/src/MagnumPlugins/AnyImageConverter/AnyImageConverter.cpp +++ b/src/MagnumPlugins/AnyImageConverter/AnyImageConverter.cpp @@ -26,11 +26,11 @@ #include "AnyImageConverter.h" #include -#include /* for Directory */ +#include /* for PluginManager */ #include #include #include -#include +#include /* for PluginMetadata::name() */ #include #include "Magnum/Trade/ImageData.h" @@ -38,6 +38,8 @@ namespace Magnum { namespace Trade { +using namespace Containers::Literals; + AnyImageConverter::AnyImageConverter(PluginManager::Manager& manager): AbstractImageConverter{manager} {} AnyImageConverter::AnyImageConverter(PluginManager::AbstractManager& manager, const std::string& plugin): AbstractImageConverter{manager, plugin} {} @@ -51,30 +53,31 @@ ImageConverterFeatures AnyImageConverter::doFeatures() const { bool AnyImageConverter::doConvertToFile(const ImageView2D& image, const Containers::StringView filename) { CORRADE_INTERNAL_ASSERT(manager()); - /** @todo lowercase only the extension, once Directory::split() is done */ - const std::string normalized = Utility::String::lowercase(filename); + /** @todo once Directory is std::string-free, use splitExtension(), but + only if we don't detect more than one extension yet */ + const Containers::String normalized = Utility::String::lowercase(filename); /* Detect the plugin from extension */ - std::string plugin; - if(Utility::String::endsWith(normalized, ".bmp")) - plugin = "BmpImageConverter"; - else if(Utility::String::endsWith(normalized, ".basis")) - plugin = "BasisImageConverter"; - else if(Utility::String::endsWith(normalized, ".exr")) - plugin = "OpenExrImageConverter"; - else if(Utility::String::endsWith(normalized, ".hdr")) - plugin = "HdrImageConverter"; - else if(Utility::String::endsWith(normalized, ".jpg") || - Utility::String::endsWith(normalized, ".jpeg") || - Utility::String::endsWith(normalized, ".jpe")) - plugin = "JpegImageConverter"; - else if(Utility::String::endsWith(normalized, ".png")) - plugin = "PngImageConverter"; - else if(Utility::String::endsWith(normalized, ".tga") || - Utility::String::endsWith(normalized, ".vda") || - Utility::String::endsWith(normalized, ".icb") || - Utility::String::endsWith(normalized, ".vst")) - plugin = "TgaImageConverter"; + Containers::StringView plugin; + if(normalized.hasSuffix(".bmp"_s)) + plugin = "BmpImageConverter"_s; + else if(normalized.hasSuffix(".basis"_s)) + plugin = "BasisImageConverter"_s; + else if(normalized.hasSuffix(".exr"_s)) + plugin = "OpenExrImageConverter"_s; + else if(normalized.hasSuffix(".hdr"_s)) + plugin = "HdrImageConverter"_s; + else if(normalized.hasSuffix(".jpg"_s) || + normalized.hasSuffix(".jpeg"_s) || + normalized.hasSuffix(".jpe"_s)) + plugin = "JpegImageConverter"_s; + else if(normalized.hasSuffix(".png"_s)) + plugin = "PngImageConverter"_s; + else if(normalized.hasSuffix(".tga"_s) || + normalized.hasSuffix(".vda"_s) || + normalized.hasSuffix(".icb"_s) || + normalized.hasSuffix( ".vst"_s)) + plugin = "TgaImageConverter"_s; else { Error{} << "Trade::AnyImageConverter::convertToFile(): cannot determine the format of" << filename; return false; diff --git a/src/MagnumPlugins/AnySceneConverter/AnySceneConverter.cpp b/src/MagnumPlugins/AnySceneConverter/AnySceneConverter.cpp index a96587dfc..2c1c450a4 100644 --- a/src/MagnumPlugins/AnySceneConverter/AnySceneConverter.cpp +++ b/src/MagnumPlugins/AnySceneConverter/AnySceneConverter.cpp @@ -26,11 +26,11 @@ #include "AnySceneConverter.h" #include -#include +#include /* for PluginManager */ #include #include #include -#include +#include /* for PluginMetadata::name() */ #include #include "Magnum/Trade/ImageData.h" @@ -38,6 +38,8 @@ namespace Magnum { namespace Trade { +using namespace Containers::Literals; + AnySceneConverter::AnySceneConverter(PluginManager::Manager& manager): AbstractSceneConverter{manager} {} AnySceneConverter::AnySceneConverter(PluginManager::AbstractManager& manager, const std::string& plugin): AbstractSceneConverter{manager, plugin} {} @@ -51,13 +53,14 @@ SceneConverterFeatures AnySceneConverter::doFeatures() const { bool AnySceneConverter::doConvertToFile(const MeshData& mesh, const Containers::StringView filename) { CORRADE_INTERNAL_ASSERT(manager()); - /** @todo lowercase only the extension, once Directory::split() is done */ - const std::string normalized = Utility::String::lowercase(filename); + /** @todo once Directory is std::string-free, use splitExtension(), but + only if we don't detect more than one extension yet */ + const Containers::StringView normalized = Utility::String::lowercase(filename); /* Detect the plugin from extension */ - std::string plugin; - if(Utility::String::endsWith(normalized, ".ply")) - plugin = "StanfordSceneConverter"; + Containers::StringView plugin; + if(normalized.hasSuffix(".ply"_s)) + plugin = "StanfordSceneConverter"_s; else { Error{} << "Trade::AnySceneConverter::convertToFile(): cannot determine the format of" << filename; return false; diff --git a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp index c5c7888db..58a512bad 100644 --- a/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp +++ b/src/MagnumPlugins/AnyShaderConverter/AnyConverter.cpp @@ -27,11 +27,10 @@ #include #include -#include #include #include #include -#include +#include /* for PluginMetadata::name() */ #include #include @@ -122,49 +121,50 @@ Containers::StringView stringForFormat(const Format format) { } Format formatForExtension(const char* prefix, const Containers::StringView filename) { - /** @todo lowercase only the extension, once Directory::split() is done */ - const std::string normalized = Utility::String::lowercase(filename); + /* Can't reliably lowercase just the extension as we detect double + extensions as well */ + const Containers::String normalized = Utility::String::lowercase(filename); /* https://github.com/KhronosGroup/SPIRV-Tools/blob/a715b1b4053519ad0f2bdb2d22ace35d35867cff/README.md#command-line-tools "It's a convention to name SPIR-V assembly and binary files with suffix .spvasm and .spv, respectively." IT'S GREAT THAT I HAD TO SEARCH HALF THE INTERNET TO FIND THIS CONVENTION. Especially when tests in the SPIRV-Cross repo use `.asm.bla` instead, FFS. */ - if(Utility::String::endsWith(normalized, ".spvasm") || + if(normalized.hasSuffix(".spvasm"_s) || /* Not official, used by https://github.com/KhronosGroup/SPIRV-Cross */ - Utility::String::endsWith(normalized, ".asm.vert") || - Utility::String::endsWith(normalized, ".asm.frag") || - Utility::String::endsWith(normalized, ".asm.geom") || - Utility::String::endsWith(normalized, ".asm.comp") || - Utility::String::endsWith(normalized, ".asm.tesc") || - Utility::String::endsWith(normalized, ".asm.tese") || - Utility::String::endsWith(normalized, ".asm.rgen") || - Utility::String::endsWith(normalized, ".asm.rint") || - Utility::String::endsWith(normalized, ".asm.rahit") || - Utility::String::endsWith(normalized, ".asm.rchit") || - Utility::String::endsWith(normalized, ".asm.rmiss") || - Utility::String::endsWith(normalized, ".asm.rcall") || - Utility::String::endsWith(normalized, ".asm.mesh") || - Utility::String::endsWith(normalized, ".asm.task")) + normalized.hasSuffix(".asm.vert"_s) || + normalized.hasSuffix(".asm.frag"_s) || + normalized.hasSuffix(".asm.geom"_s) || + normalized.hasSuffix(".asm.comp"_s) || + normalized.hasSuffix(".asm.tesc"_s) || + normalized.hasSuffix(".asm.tese"_s) || + normalized.hasSuffix(".asm.rgen"_s) || + normalized.hasSuffix(".asm.rint"_s) || + normalized.hasSuffix(".asm.rahit"_s) || + normalized.hasSuffix(".asm.rchit"_s) || + normalized.hasSuffix(".asm.rmiss"_s) || + normalized.hasSuffix(".asm.rcall"_s) || + normalized.hasSuffix(".asm.mesh"_s) || + normalized.hasSuffix(".asm.task"_s)) return Format::SpirvAssembly; /* https://github.com/KhronosGroup/glslang/blob/3ce148638bdc3807316e358dee4a5c9583189ae7/StandAlone/StandAlone.cpp#L260-L274 */ - else if(Utility::String::endsWith(normalized, ".glsl") || - Utility::String::endsWith(normalized, ".vert") || - Utility::String::endsWith(normalized, ".frag") || - Utility::String::endsWith(normalized, ".geom") || - Utility::String::endsWith(normalized, ".comp") || - Utility::String::endsWith(normalized, ".tesc") || - Utility::String::endsWith(normalized, ".tese") || - Utility::String::endsWith(normalized, ".rgen") || - Utility::String::endsWith(normalized, ".rint") || - Utility::String::endsWith(normalized, ".rahit") || - Utility::String::endsWith(normalized, ".rchit") || - Utility::String::endsWith(normalized, ".rmiss") || - Utility::String::endsWith(normalized, ".rcall") || - Utility::String::endsWith(normalized, ".mesh") || - Utility::String::endsWith(normalized, ".task")) + else if(normalized.hasSuffix(".glsl"_s) || + normalized.hasSuffix(".vert"_s) || + normalized.hasSuffix(".frag"_s) || + normalized.hasSuffix(".geom"_s) || + normalized.hasSuffix(".comp"_s) || + normalized.hasSuffix(".tesc"_s) || + normalized.hasSuffix(".tese"_s) || + normalized.hasSuffix(".rgen"_s) || + normalized.hasSuffix(".rint"_s) || + normalized.hasSuffix(".rahit"_s) || + normalized.hasSuffix(".rchit"_s) || + normalized.hasSuffix(".rmiss"_s) || + normalized.hasSuffix(".rcall"_s) || + normalized.hasSuffix(".mesh"_s) || + normalized.hasSuffix(".task"_s)) return Format::Glsl; - else if(Utility::String::endsWith(normalized, ".spv")) + else if(normalized.hasSuffix(".spv"_s)) return Format::Spirv; Error{} << prefix << "cannot determine the format of" << filename; From 48838c442fea70e667a32f4e1bb64bd50a119295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 28 Jun 2021 11:07:49 +0200 Subject: [PATCH 91/93] Adapt to Corrade changes. --- doc/generated/easings.cpp | 2 +- src/Magnum/GL/Context.cpp | 6 +++--- src/Magnum/Vk/Device.cpp | 6 +++--- src/Magnum/Vk/Instance.cpp | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/generated/easings.cpp b/doc/generated/easings.cpp index 6e925cb6d..90ad058e4 100644 --- a/doc/generated/easings.cpp +++ b/doc/generated/easings.cpp @@ -204,7 +204,7 @@ const Color3 success = 0x3bd267_srgbf; int main() { using namespace Animation::Easing; - #define _c(name) Utility::String::lowercase(#name), name + #define _c(name) Utility::String::lowercase(std::string{#name}), name generate(_c(linear), {}, /* [linear] */ CubicBezier2D{Vector2{0.0f}, Vector2{1.0f/3.0f}, diff --git a/src/Magnum/GL/Context.cpp b/src/Magnum/GL/Context.cpp index 2fbc7d6b2..fdeafb5b8 100644 --- a/src/Magnum/GL/Context.cpp +++ b/src/Magnum/GL/Context.cpp @@ -719,7 +719,7 @@ Context::Context(NoCreateT, Utility::Arguments& args, Int argc, const char** arg bother with String allocations. */ const Containers::StringView disabledWorkarounds = args.value("disable-workarounds"); if(!disabledWorkarounds.isEmpty()) { - const Containers::Array split = disabledWorkarounds.splitWithoutEmptyParts(); + const Containers::Array split = disabledWorkarounds.splitOnWhitespaceWithoutEmptyParts(); arrayReserve(_driverWorkarounds, split.size()); for(const Containers::StringView workaround: split) disableDriverWorkaround(workaround); @@ -730,7 +730,7 @@ Context::Context(NoCreateT, Utility::Arguments& args, Int argc, const char** arg and another binary search in tryCreate(). */ const Containers::StringView disabledExtensions = args.value("disable-extensions"); if(!disabledExtensions.isEmpty()) { - const Containers::Array split = disabledExtensions.splitWithoutEmptyParts(); + const Containers::Array split = disabledExtensions.splitOnWhitespaceWithoutEmptyParts(); arrayReserve(_disabledExtensions, split.size()); for(const Containers::StringView extension: split) { if(const Extension* found = findExtension(extension)) { @@ -1074,7 +1074,7 @@ Containers::Array Context::extensionStrings() const { #ifndef MAGNUM_TARGET_GLES3 /* OpenGL 2.1 / OpenGL ES 2.0 doesn't have glGetStringi() */ - return Containers::StringView{reinterpret_cast(glGetString(GL_EXTENSIONS)), Containers::StringViewFlag::Global}.splitWithoutEmptyParts(); + return Containers::StringView{reinterpret_cast(glGetString(GL_EXTENSIONS)), Containers::StringViewFlag::Global}.splitOnWhitespaceWithoutEmptyParts(); #endif } diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index 245982a93..5c4357e4a 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -141,7 +141,7 @@ DeviceCreateInfo::DeviceCreateInfo(DeviceProperties& deviceProperties, const Ext we don't need to bother with String allocations. */ Containers::StringView disabledWorkarounds = args.value("disable-workarounds"); if(!disabledWorkarounds.isEmpty()) { - const Containers::Array split = disabledWorkarounds.splitWithoutEmptyParts(); + const Containers::Array split = disabledWorkarounds.splitOnWhitespaceWithoutEmptyParts(); arrayReserve(_state->encounteredWorkarounds, split.size()); for(const Containers::StringView workaround: split) Implementation::disableWorkaround(_state->encounteredWorkarounds, workaround); @@ -152,7 +152,7 @@ DeviceCreateInfo::DeviceCreateInfo(DeviceProperties& deviceProperties, const Ext Containers::String disabledExtensions = args.value("disable-extensions"); if(!disabledExtensions.isEmpty()) { _state->disabledExtensionsStorage = std::move(disabledExtensions); - _state->disabledExtensions = Containers::StringView{_state->disabledExtensionsStorage}.splitWithoutEmptyParts(); + _state->disabledExtensions = Containers::StringView{_state->disabledExtensionsStorage}.splitOnWhitespaceWithoutEmptyParts(); std::sort(_state->disabledExtensions.begin(), _state->disabledExtensions.end()); } @@ -164,7 +164,7 @@ DeviceCreateInfo::DeviceCreateInfo(DeviceProperties& deviceProperties, const Ext allocation-free, the strings will be turned into owning copies because none of them is null-terminated or global -- could be a better idea to just grow one giant string internally (once we have growable strings) */ - addEnabledExtensions(args.value("enable-extensions").splitWithoutEmptyParts()); + addEnabledExtensions(args.value("enable-extensions").splitOnWhitespaceWithoutEmptyParts()); /* Enable implicit extensions unless that's forbidden */ /** @todo move this somewhere else as this will grow significantly? */ diff --git a/src/Magnum/Vk/Instance.cpp b/src/Magnum/Vk/Instance.cpp index d89ad4a36..b1202d39e 100644 --- a/src/Magnum/Vk/Instance.cpp +++ b/src/Magnum/Vk/Instance.cpp @@ -104,14 +104,14 @@ InstanceCreateInfo::InstanceCreateInfo(const Int argc, const char** const argv, if(!_state) _state.emplace(); _state->disabledLayersStorage = std::move(disabledLayers); - _state->disabledLayers = Containers::StringView{_state->disabledLayersStorage}.splitWithoutEmptyParts(); + _state->disabledLayers = Containers::StringView{_state->disabledLayersStorage}.splitOnWhitespaceWithoutEmptyParts(); std::sort(_state->disabledLayers.begin(), _state->disabledLayers.end()); } if(!disabledExtensions.isEmpty()) { if(!_state) _state.emplace(); _state->disabledExtensionsStorage = std::move(disabledExtensions); - _state->disabledExtensions = Containers::StringView{_state->disabledExtensionsStorage}.splitWithoutEmptyParts(); + _state->disabledExtensions = Containers::StringView{_state->disabledExtensionsStorage}.splitOnWhitespaceWithoutEmptyParts(); std::sort(_state->disabledExtensions.begin(), _state->disabledExtensions.end()); } @@ -123,8 +123,8 @@ InstanceCreateInfo::InstanceCreateInfo(const Int argc, const char** const argv, allocation-free, the strings will be turned into owning copies because none of them is null-terminated or global -- could be a better idea to just grow one giant string internally (once we have growable strings) */ - addEnabledLayers(args.value("enable-layers").splitWithoutEmptyParts()); - addEnabledExtensions(args.value("enable-instance-extensions").splitWithoutEmptyParts()); + addEnabledLayers(args.value("enable-layers").splitOnWhitespaceWithoutEmptyParts()); + addEnabledExtensions(args.value("enable-instance-extensions").splitOnWhitespaceWithoutEmptyParts()); /** @todo use this (enabling debug layers etc.) */ static_cast(layerProperties); From c1c02dea5818be742981b309561cbc5930e3e693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 28 Jun 2021 19:08:57 +0200 Subject: [PATCH 92/93] Trade: make Abstract{Image,Scene}Converter::doConvertToFile() protected. This is already done for the AbstractImporter and the new AbstractShaderConverter, as there's a common use case of checking just the filename for input/output path or file type detection and then delegating to the common implementation working directly on data. --- doc/changelog.dox | 7 ++ src/Magnum/Trade/AbstractImageConverter.h | 136 ++++++++++++---------- src/Magnum/Trade/AbstractSceneConverter.h | 22 ++-- 3 files changed, 96 insertions(+), 69 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index f793a0160..9820f2f47 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -335,6 +335,13 @@ See also: @subsubsection changelog-latest-changes-trade Trade library +- @ref Trade::AbstractImageConverter::doConvertToFile() and + @ref Trade::AbstractSceneConverter::doConvertToFile() are now + @cpp protected @ce instead of @cpp private @ce to allow calling them from + plugin implementations and reuse the provided fallback to + @relativeref{Trade::AbstractImageConverter,doConvertToData()}, for example + when the implementation only neeeds to do a format detection based on file + extension - Recognizing TIFF file header magic in @ref Trade::AnyImageImporter "AnyImageImporter" - @ref Audio::AnyImporter "AnyAudioImporter", @relativeref{Trade,AnyImageImporter}, @relativeref{Trade,AnyImageConverter}, diff --git a/src/Magnum/Trade/AbstractImageConverter.h b/src/Magnum/Trade/AbstractImageConverter.h index e44adc466..0e4ad45a2 100644 --- a/src/Magnum/Trade/AbstractImageConverter.h +++ b/src/Magnum/Trade/AbstractImageConverter.h @@ -870,6 +870,82 @@ class MAGNUM_TRADE_EXPORT AbstractImageConverter: public PluginManager::Abstract */ bool convertToFile(const ImageData3D& image, Containers::StringView filename); + protected: + /** + * @brief Implementation for @ref convertToFile(const ImageView1D&, Containers::StringView) + * @m_since_latest + * + * If @ref ImageConverterFeature::Convert1DToData is supported, default + * implementation calls @ref doConvertToData(const ImageView1D&) and + * saves the result to given file. It is allowed to call this function + * from your @ref doConvertToFile() implementation, for example when + * you only need to do format detection based on file extension. + */ + virtual bool doConvertToFile(const ImageView1D& image, Containers::StringView filename); + + /** + * @brief Implementation for @ref convertToFile(const ImageView2D&, Containers::StringView) + * @m_since_latest + * + * If @ref ImageConverterFeature::Convert2DToData is supported, default + * implementation calls @ref doConvertToData(const ImageView2D&) and + * saves the result to given file. It is allowed to call this function + * from your @ref doConvertToFile() implementation, for example when + * you only need to do format detection based on file extension. + */ + virtual bool doConvertToFile(const ImageView2D& image, Containers::StringView filename); + + /** + * @brief Implementation for @ref convertToFile(const ImageView3D&, Containers::StringView) + * @m_since_latest + * + * If @ref ImageConverterFeature::Convert3DToData is supported, default + * implementation calls @ref doConvertToData(const ImageView3D&) and + * saves the result to given file. It is allowed to call this function + * from your @ref doConvertToFile() implementation, for example when + * you only need to do format detection based on file extension. + */ + virtual bool doConvertToFile(const ImageView3D& image, Containers::StringView filename); + + /** + * @brief Implementation for @ref convertToFile(const CompressedImageView1D&, Containers::StringView) + * @m_since_latest + * + * If @ref ImageConverterFeature::ConvertCompressed1DToData is + * supported, default implementation calls @ref doConvertToData(const CompressedImageView1D&) + * and saves the result to given file. It is allowed to call this + * function from your @ref doConvertToFile() implementation, for + * example when you only need to do format detection based on file + * extension. + */ + virtual bool doConvertToFile(const CompressedImageView1D& image, Containers::StringView filename); + + /** + * @brief Implementation for @ref convertToFile(const CompressedImageView2D&, Containers::StringView) + * @m_since_latest + * + * If @ref ImageConverterFeature::ConvertCompressed2DToData is + * supported, default implementation calls @ref doConvertToData(const CompressedImageView2D&) + * and saves the result to given file. It is allowed to call this + * function from your @ref doConvertToFile() implementation, for + * example when you only need to do format detection based on file + * extension. + */ + virtual bool doConvertToFile(const CompressedImageView2D& image, Containers::StringView filename); + + /** + * @brief Implementation for @ref convertToFile(const CompressedImageView3D&, Containers::StringView) + * @m_since_latest + * + * If @ref ImageConverterFeature::ConvertCompressed3DToData is + * supported, default implementation calls @ref doConvertToData(const CompressedImageView3D&) + * and saves the result to given file. It is allowed to call this + * function from your @ref doConvertToFile() implementation, for + * example when you only need to do format detection based on file + * extension. + */ + virtual bool doConvertToFile(const CompressedImageView3D& image, Containers::StringView filename); + private: /** @brief Implementation for @ref features() */ virtual ImageConverterFeatures doFeatures() const = 0; @@ -962,66 +1038,6 @@ class MAGNUM_TRADE_EXPORT AbstractImageConverter: public PluginManager::Abstract */ virtual Containers::Array doConvertToData(const CompressedImageView3D& image); - /** - * @brief Implementation for @ref convertToFile(const ImageView1D&, Containers::StringView) - * @m_since_latest - * - * If @ref ImageConverterFeature::Convert1DToData is supported, default - * implementation calls @ref doConvertToData(const ImageView1D&) and - * saves the result to given file. - */ - virtual bool doConvertToFile(const ImageView1D& image, Containers::StringView filename); - - /** - * @brief Implementation for @ref convertToFile(const ImageView2D&, Containers::StringView) - * @m_since_latest - * - * If @ref ImageConverterFeature::Convert2DToData is supported, default - * implementation calls @ref doConvertToData(const ImageView2D&) and - * saves the result to given file. - */ - virtual bool doConvertToFile(const ImageView2D& image, Containers::StringView filename); - - /** - * @brief Implementation for @ref convertToFile(const ImageView3D&, Containers::StringView) - * @m_since_latest - * - * If @ref ImageConverterFeature::Convert3DToData is supported, default - * implementation calls @ref doConvertToData(const ImageView3D&) and - * saves the result to given file. - */ - virtual bool doConvertToFile(const ImageView3D& image, Containers::StringView filename); - - /** - * @brief Implementation for @ref convertToFile(const CompressedImageView1D&, Containers::StringView) - * @m_since_latest - * - * If @ref ImageConverterFeature::ConvertCompressed1DToData is - * supported, default implementation calls @ref doConvertToData(const CompressedImageView1D&) - * and saves the result to given file. - */ - virtual bool doConvertToFile(const CompressedImageView1D& image, Containers::StringView filename); - - /** - * @brief Implementation for @ref convertToFile(const CompressedImageView2D&, Containers::StringView) - * @m_since_latest - * - * If @ref ImageConverterFeature::ConvertCompressed2DToData is - * supported, default implementation calls @ref doConvertToData(const CompressedImageView2D&) - * and saves the result to given file. - */ - virtual bool doConvertToFile(const CompressedImageView2D& image, Containers::StringView filename); - - /** - * @brief Implementation for @ref convertToFile(const CompressedImageView3D&, Containers::StringView) - * @m_since_latest - * - * If @ref ImageConverterFeature::ConvertCompressed3DToData is - * supported, default implementation calls @ref doConvertToData(const CompressedImageView3D&) - * and saves the result to given file. - */ - virtual bool doConvertToFile(const CompressedImageView3D& image, Containers::StringView filename); - ImageConverterFlags _flags; }; diff --git a/src/Magnum/Trade/AbstractSceneConverter.h b/src/Magnum/Trade/AbstractSceneConverter.h index b5587f4af..c1f13c7f7 100644 --- a/src/Magnum/Trade/AbstractSceneConverter.h +++ b/src/Magnum/Trade/AbstractSceneConverter.h @@ -318,6 +318,19 @@ class MAGNUM_TRADE_EXPORT AbstractSceneConverter: public PluginManager::Abstract CORRADE_DEPRECATED("use convertToFile(const MeshData&, Containers::StringView) instead") bool convertToFile(const std::string& filename, const MeshData& mesh); #endif + protected: + /** + * @brief Implementation for @ref convertToFile(const MeshData&, Containers::StringView) + * + * If @ref SceneConverterFeature::ConvertMeshToData is supported, + * default implementation calls @ref doConvertToData(const MeshData&) + * and saves the result to given file. It is allowed to call this + * function from your @ref doConvertToFile() implementation, for + * example when you only need to do format detection based on file + * extension. + */ + virtual bool doConvertToFile(const MeshData& mesh, Containers::StringView filename); + private: /** * @brief Implementation for @ref features() @@ -350,15 +363,6 @@ class MAGNUM_TRADE_EXPORT AbstractSceneConverter: public PluginManager::Abstract /** @brief Implementation for @ref convertToData(const MeshData&) */ virtual Containers::Array doConvertToData(const MeshData& mesh); - /** - * @brief Implementation for @ref convertToFile(const MeshData&, Containers::StringView) - * - * If @ref SceneConverterFeature::ConvertMeshToData is supported, - * default implementation calls @ref doConvertToData(const MeshData&) - * and saves the result to given file. - */ - virtual bool doConvertToFile(const MeshData& mesh, Containers::StringView filename); - SceneConverterFlags _flags; }; From 047c539a17b9b68d9bf6526b8974bf285956010f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 28 Jun 2021 23:45:16 +0200 Subject: [PATCH 93/93] =?UTF-8?q?=C3=84nyImageImporter:=20recognize=20BMP?= =?UTF-8?q?=20header=20magic.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I needed to know the signature for a StbImageConverter test, so why not add it here as well now that I managed to figure that out. --- doc/changelog.dox | 2 +- .../AnyImageImporter/AnyImageImporter.cpp | 3 +++ .../AnyImageImporter/AnyImageImporter.h | 3 ++- .../Test/AnyImageImporterTest.cpp | 3 ++- .../AnyImageImporter/Test/CMakeLists.txt | 1 + src/MagnumPlugins/AnyImageImporter/Test/rgb.bmp | Bin 0 -> 162 bytes 6 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 src/MagnumPlugins/AnyImageImporter/Test/rgb.bmp diff --git a/doc/changelog.dox b/doc/changelog.dox index 9820f2f47..55a855c53 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -342,7 +342,7 @@ See also: @relativeref{Trade::AbstractImageConverter,doConvertToData()}, for example when the implementation only neeeds to do a format detection based on file extension -- Recognizing TIFF file header magic in @ref Trade::AnyImageImporter "AnyImageImporter" +- Recognizing BMP and TIFF file header magic in @relativeref{Trade,AnyImageImporter} - @ref Audio::AnyImporter "AnyAudioImporter", @relativeref{Trade,AnyImageImporter}, @relativeref{Trade,AnyImageConverter}, @relativeref{Trade,AnySceneImporter}, @relativeref{Trade,AnySceneConverter} diff --git a/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp b/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp index 877852f55..097a41465 100644 --- a/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp +++ b/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp @@ -162,6 +162,9 @@ void AnyImageImporter::doOpenData(Containers::ArrayView data) { /* https://github.com/BinomialLLC/basis_universal/blob/7d784c728844c007d8c95d63231f7adcc0f65364/transcoder/basisu_file_headers.h#L78 */ if(dataString.hasPrefix("sB"_s)) plugin = "BasisImporter"; + /* https://en.wikipedia.org/wiki/BMP_file_format#Bitmap_file_header */ + else if(dataString.hasPrefix("BM"_s)) + plugin = "BmpImporter"; /* https://docs.microsoft.com/cs-cz/windows/desktop/direct3ddds/dx-graphics-dds-pguide */ else if(dataString.hasPrefix("DDS "_s)) plugin = "DdsImporter"; diff --git a/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.h b/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.h index 01637d825..abbbb5c63 100644 --- a/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.h +++ b/src/MagnumPlugins/AnyImageImporter/AnyImageImporter.h @@ -58,7 +58,8 @@ tries to open the file with it. Supported formats: - Basis Universal (`*.basis` or data with corresponding signature), loaded with @ref BasisImporter or any other plugin that provides it -- Windows Bitmap (`*.bmp`), loaded with any plugin that provides `BmpImporter` +- Windows Bitmap (`*.bmp` or data with corresponding signature), loaded with + any plugin that provides `BmpImporter` - DirectDraw Surface (`*.dds` or data with corresponding signature), loaded with @ref DdsImporter or any other plugin that provides it - Graphics Interchange Format (`*.gif`), loaded with any plugin that provides diff --git a/src/MagnumPlugins/AnyImageImporter/Test/AnyImageImporterTest.cpp b/src/MagnumPlugins/AnyImageImporter/Test/AnyImageImporterTest.cpp index d210a440c..c57a205a4 100644 --- a/src/MagnumPlugins/AnyImageImporter/Test/AnyImageImporterTest.cpp +++ b/src/MagnumPlugins/AnyImageImporter/Test/AnyImageImporterTest.cpp @@ -96,7 +96,8 @@ constexpr struct { {"ICO", "pngs.ico", nullptr, "IcoImporter"}, {"DDS", "rgba_dxt1.dds", nullptr, "DdsImporter"}, {"DDS data", "rgba_dxt1.dds", fileCallback, "DdsImporter"}, - {"BMP", "image.bmp", nullptr, "BmpImporter"}, + {"BMP", "rgb.bmp", nullptr, "BmpImporter"}, + {"BMP data", "rgb.bmp", fileCallback, "BmpImporter"}, {"GIF", "image.gif", nullptr, "GifImporter"}, {"PSD", "image.psd", nullptr, "PsdImporter"}, {"TIFF", "image.tiff", nullptr, "TiffImporter"}, diff --git a/src/MagnumPlugins/AnyImageImporter/Test/CMakeLists.txt b/src/MagnumPlugins/AnyImageImporter/Test/CMakeLists.txt index 1510bb328..e70e85c26 100644 --- a/src/MagnumPlugins/AnyImageImporter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/AnyImageImporter/Test/CMakeLists.txt @@ -59,6 +59,7 @@ corrade_add_test(AnyImageImporterTest AnyImageImporterTest.cpp image.tiff pngs.ico rgb.basis + rgb.bmp rgb.hdr rgb.png rgb.tga diff --git a/src/MagnumPlugins/AnyImageImporter/Test/rgb.bmp b/src/MagnumPlugins/AnyImageImporter/Test/rgb.bmp new file mode 100644 index 0000000000000000000000000000000000000000..f0c917a14c544e3511f42d67bd2dd9013e148df2 GIT binary patch literal 162 zcmZ?rUBmzZT|lY^h?#+y35XdPB!DCXlms)8i2n=>5CjtY@8lj-+;HftM#2s`QH9pm zj1I=e!VPI@Y7S{>Yzt=2