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