From 96b5bd2d8696bd8466600084cc8ddfaae6515c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 16 Dec 2022 14:06:48 +0100 Subject: [PATCH] Shaders: allow uploading just a subset of joint matrices. It's not a GL error, and allows the application to compile just a single shader for all skinned meshes, not one for each skeleton size. Together with the dynamic per-vertex joint count this means the app only needs a single shader for all skinned meshes, which is nice. --- src/Magnum/Shaders/FlatGL.cpp | 4 +- src/Magnum/Shaders/FlatGL.h | 31 +++++++------ src/Magnum/Shaders/MeshVisualizerGL.cpp | 8 ++-- src/Magnum/Shaders/MeshVisualizerGL.h | 46 ++++++++++--------- src/Magnum/Shaders/PhongGL.cpp | 4 +- src/Magnum/Shaders/PhongGL.h | 28 ++++++----- src/Magnum/Shaders/Test/FlatGLTest.cpp | 15 ++++-- .../Shaders/Test/MeshVisualizerGLTest.cpp | 23 +++++++--- src/Magnum/Shaders/Test/PhongGLTest.cpp | 13 ++++-- 9 files changed, 103 insertions(+), 69 deletions(-) diff --git a/src/Magnum/Shaders/FlatGL.cpp b/src/Magnum/Shaders/FlatGL.cpp index 93f6937b7..637afb300 100644 --- a/src/Magnum/Shaders/FlatGL.cpp +++ b/src/Magnum/Shaders/FlatGL.cpp @@ -498,8 +498,8 @@ template FlatGL& FlatGL::setObje template FlatGL& FlatGL::setJointMatrices(const Containers::ArrayView> matrices) { CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), "Shaders::FlatGL::setJointMatrices(): the shader was created with uniform buffers enabled", *this); - CORRADE_ASSERT(_jointCount == matrices.size(), - "Shaders::FlatGL::setJointMatrices(): expected" << _jointCount << "items but got" << matrices.size(), *this); + CORRADE_ASSERT(matrices.size() <= _jointCount, + "Shaders::FlatGL::setJointMatrices(): expected at most" << _jointCount << "items but got" << matrices.size(), *this); if(_jointCount) setUniform(_jointMatricesUniform, matrices); return *this; } diff --git a/src/Magnum/Shaders/FlatGL.h b/src/Magnum/Shaders/FlatGL.h index 7ead11daa..892dd811e 100644 --- a/src/Magnum/Shaders/FlatGL.h +++ b/src/Magnum/Shaders/FlatGL.h @@ -152,10 +152,13 @@ per-vertex primary and secondary joint count in @ref Configuration::setJointCount() and upload appropriate joint matrices with @ref setJointMatrices(). -To avoid having to compile multiple shader variants for different per-vertex -joint counts, enable @ref Flag::DynamicPerVertexJointCount, set the maximum -per-vertex joint count in @ref Configuration::setJointCount() and then adjust -the actual per-draw joint count with @ref setPerVertexJointCount(). +To avoid having to compile multiple shader variants for different joint matrix +counts, set the maximum used joint count in @ref Configuration::setJointCount() +and then upload just a prefix via @ref setJointMatrices(). Similarly, to avoid +multiple variants for different per-vertex joint counts, enable +@ref Flag::DynamicPerVertexJointCount, set the maximum per-vertex joint count +in @ref Configuration::setJointCount() and then adjust the actual per-draw +joint count with @ref setPerVertexJointCount(). @requires_gl30 Extension @gl_extension{EXT,texture_integer} @requires_gles30 Skinning requires integer support in shaders, which is not @@ -1015,7 +1018,7 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: public GL:: * @m_since_latest * * Initial values are identity transformations. Expects that the size - * of the @p matrices array is the same as @ref jointCount(). + * of the @p matrices array is not larger than @ref jointCount(). * * Expects that @ref Flag::UniformBuffers is not set, in that case fill * @ref TransformationUniform2D::transformationMatrix / @@ -1393,15 +1396,15 @@ template class MAGNUM_SHADERS_EXPORT FlatGL: /** * @brief Set joint count * - * If @ref Flag::UniformBuffers isn't set, @p count describes how many - * joint matrices get supplied to each draw by @ref setJointMatrices() - * / @ref setJointMatrix(). If @ref Flag::UniformBuffers is set, - * @p count describes size of a @ref TransformationUniform2D / - * @ref TransformationUniform3D buffer bound with - * @ref bindJointBuffer(); as uniform buffers are required to have a - * statically defined size. The per-vertex joints then index into the - * array offset by @ref FlatDrawUniform::jointOffset. If @p count is - * @cpp 0 @ce, skinning is not performed. + * If @ref Flag::UniformBuffers isn't set, @p count describes an upper + * bound on how many joint matrices get supplied to each draw by + * @ref setJointMatrices() / @ref setJointMatrix(). If + * @ref Flag::UniformBuffers is set, @p count describes size of a + * @ref TransformationUniform2D / @ref TransformationUniform3D buffer + * bound with @ref bindJointBuffer(); as uniform buffers are required + * to have a statically defined size. The per-vertex joints then index + * into the array offset by @ref FlatDrawUniform::jointOffset. If + * @p count is @cpp 0 @ce, skinning is not performed. * * The @p perVertexCount and @p secondaryPerVertexCount then describe * how many components are taken from @ref JointIds / @ref Weights and diff --git a/src/Magnum/Shaders/MeshVisualizerGL.cpp b/src/Magnum/Shaders/MeshVisualizerGL.cpp index 85d516206..e5a1868fd 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.cpp +++ b/src/Magnum/Shaders/MeshVisualizerGL.cpp @@ -803,8 +803,8 @@ MeshVisualizerGL2D& MeshVisualizerGL2D::setSmoothness(const Float smoothness) { MeshVisualizerGL2D& MeshVisualizerGL2D::setJointMatrices(const Containers::ArrayView matrices) { CORRADE_ASSERT(!(flags() >= Flag::UniformBuffers), "Shaders::MeshVisualizerGL2D::setJointMatrices(): the shader was created with uniform buffers enabled", *this); - CORRADE_ASSERT(_jointCount == matrices.size(), - "Shaders::MeshVisualizerGL2D::setJointMatrices(): expected" << _jointCount << "items but got" << matrices.size(), *this); + CORRADE_ASSERT(matrices.size() <= _jointCount, + "Shaders::MeshVisualizerGL2D::setJointMatrices(): expected at most" << _jointCount << "items but got" << matrices.size(), *this); if(_jointCount) setUniform(_jointMatricesUniform, matrices); return *this; } @@ -1372,8 +1372,8 @@ MeshVisualizerGL3D& MeshVisualizerGL3D::setSmoothness(const Float smoothness) { MeshVisualizerGL3D& MeshVisualizerGL3D::setJointMatrices(const Containers::ArrayView matrices) { CORRADE_ASSERT(!(flags() >= Flag::UniformBuffers), "Shaders::MeshVisualizerGL3D::setJointMatrices(): the shader was created with uniform buffers enabled", *this); - CORRADE_ASSERT(_jointCount == matrices.size(), - "Shaders::MeshVisualizerGL3D::setJointMatrices(): expected" << _jointCount << "items but got" << matrices.size(), *this); + CORRADE_ASSERT(matrices.size() <= _jointCount, + "Shaders::MeshVisualizerGL3D::setJointMatrices(): expected at most" << _jointCount << "items but got" << matrices.size(), *this); if(_jointCount) setUniform(_jointMatricesUniform, matrices); return *this; } diff --git a/src/Magnum/Shaders/MeshVisualizerGL.h b/src/Magnum/Shaders/MeshVisualizerGL.h index 665b10356..7fc6d3a83 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.h +++ b/src/Magnum/Shaders/MeshVisualizerGL.h @@ -897,7 +897,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua * @m_since_latest * * Initial values are identity transformations. Expects that the size - * of the @p matrices array is the same as @ref jointCount(). + * of the @p matrices array is not larger than @ref jointCount(). * * Expects that @ref Flag::UniformBuffers is not set, in that case fill * @ref TransformationUniform2D::transformationMatrix and call @@ -1193,13 +1193,13 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D::Configuration { /** * @brief Set joint count * - * If @ref Flag::UniformBuffers isn't set, @p count describes how many - * joint matrices get supplied to each draw by @ref setJointMatrices() - * / @ref setJointMatrix(). If @ref Flag::UniformBuffers is set, - * @p count describes size of a @ref TransformationUniform2D buffer - * bound with @ref bindJointBuffer(); as uniform buffers are required - * to have a statically defined size. The per-vertex joints then index - * into the array offset by + * If @ref Flag::UniformBuffers isn't set, @p count describes an upper + * bound on how many joint matrices get supplied to each draw with + * @ref setJointMatrices() / @ref setJointMatrix(). If + * @ref Flag::UniformBuffers is set, @p count describes size of a + * @ref TransformationUniform2D buffer bound with @ref bindJointBuffer(); + * as uniform buffers are required to have a statically defined size. + * The per-vertex joints then index into the array offset by * @ref MeshVisualizerDrawUniform2D::jointOffset. If @p count is * @cpp 0 @ce, skinning is not performed. * @@ -1509,10 +1509,13 @@ per-vertex primary and secondary joint count in transforming the mesh vertices for feature parity with other shaders, no skinning-specific visualization feature is implemented. -To avoid having to compile multiple shader variants for different per-vertex -joint counts, enable @ref Flag::DynamicPerVertexJointCount, set the maximum -per-vertex joint count in @ref Configuration::setJointCount() and then adjust -the actual per-draw joint count with @ref setPerVertexJointCount(). +To avoid having to compile multiple shader variants for different joint matrix +counts, set the maximum used joint count in @ref Configuration::setJointCount() +and then upload just a prefix via @ref setJointMatrices(). Similarly, to avoid +multiple variants for different per-vertex joint counts, enable +@ref Flag::DynamicPerVertexJointCount, set the maximum per-vertex joint count +in @ref Configuration::setJointCount() and then adjust the actual per-draw +joint count with @ref setPerVertexJointCount(). @requires_gl30 Extension @gl_extension{EXT,texture_integer} @requires_gles30 Skinning requires integer support in shaders, which is not @@ -2744,7 +2747,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua * @m_since_latest * * Initial values are identity transformations. Expects that the size - * of the @p matrices array is the same as @ref jointCount(). + * of the @p matrices array is not larger than @ref jointCount(). * * Expects that @ref Flag::UniformBuffers is not set, in that case fill * @ref TransformationUniform3D::transformationMatrix and call @@ -3140,14 +3143,15 @@ class MeshVisualizerGL3D::Configuration { /** * @brief Set joint count * - * If @ref Flag::UniformBuffers isn't set, @p count describes how many - * joint matrices get supplied to each draw by @ref setJointMatrices() - * / @ref setJointMatrix(). If @ref Flag::UniformBuffers is set, - * @p count describes size of a @ref TransformationUniform3D buffer - * bound with @ref bindJointBuffer(); as uniform buffers are required - * to have a statically defined size. The per-vertex joints then index - * into the array offset by @ref MeshVisualizerDrawUniform3D::jointOffset. - * If @p count is @cpp 0 @ce, skinning is not performed. + * If @ref Flag::UniformBuffers isn't set, @p count describes an upper + * bound on how many joint matrices get supplied to each draw with + * @ref setJointMatrices() / @ref setJointMatrix(). If + * @ref Flag::UniformBuffers is set, @p count describes size of a + * @ref TransformationUniform3D buffer bound with @ref bindJointBuffer(); + * as uniform buffers are required to have a statically defined size. + * The per-vertex joints then index into the array offset by + * @ref MeshVisualizerDrawUniform3D::jointOffset. If @p count is + * @cpp 0 @ce, skinning is not performed. * * The @p perVertexCount and @p secondaryPerVertexCount then describe * how many components are taken from @ref JointIds / @ref Weights and diff --git a/src/Magnum/Shaders/PhongGL.cpp b/src/Magnum/Shaders/PhongGL.cpp index aad1646a8..9ad18b6ac 100644 --- a/src/Magnum/Shaders/PhongGL.cpp +++ b/src/Magnum/Shaders/PhongGL.cpp @@ -886,8 +886,8 @@ PhongGL& PhongGL::setLightRange(const UnsignedInt id, const Float range) { PhongGL& PhongGL::setJointMatrices(const Containers::ArrayView matrices) { CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), "Shaders::PhongGL::setJointMatrices(): the shader was created with uniform buffers enabled", *this); - CORRADE_ASSERT(_jointCount == matrices.size(), - "Shaders::PhongGL::setJointMatrices(): expected" << _jointCount << "items but got" << matrices.size(), *this); + CORRADE_ASSERT(matrices.size() <= _jointCount, + "Shaders::PhongGL::setJointMatrices(): expected at most" << _jointCount << "items but got" << matrices.size(), *this); if(_jointCount) setUniform(_jointMatricesUniform, matrices); return *this; } diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index d6bfc5956..d2437ee61 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -241,10 +241,13 @@ per-vertex primary and secondary joint count in @ref Configuration::setJointCount() and upload appropriate joint matrices with @ref setJointMatrices(). -To avoid having to compile multiple shader variants for different per-vertex -joint counts, enable @ref Flag::DynamicPerVertexJointCount, set the maximum -per-vertex joint count in @ref Configuration::setJointCount() and then adjust -the actual per-draw joint count with @ref setPerVertexJointCount(). +To avoid having to compile multiple shader variants for different joint matrix +counts, set the maximum used joint count in @ref Configuration::setJointCount() +and then upload just a prefix via @ref setJointMatrices(). Similarly, to avoid +multiple variants for different per-vertex joint counts, enable +@ref Flag::DynamicPerVertexJointCount, set the maximum per-vertex joint count +in @ref Configuration::setJointCount() and then adjust the actual per-draw +joint count with @ref setPerVertexJointCount(). @requires_gl30 Extension @gl_extension{EXT,texture_integer} @requires_gles30 Object ID output requires integer support in shaders, which @@ -2175,14 +2178,15 @@ class MAGNUM_SHADERS_EXPORT PhongGL::Configuration { /** * @brief Set joint count * - * If @ref Flag::UniformBuffers isn't set, @p count describes how many - * joint matrices get supplied to each draw by @ref setJointMatrices() - * / @ref setJointMatrix(). If @ref Flag::UniformBuffers is set, - * @p count describes size of a @ref TransformationUniform3D buffer - * bound with @ref bindJointBuffer(); as uniform buffers are required - * to have a statically defined size. The per-vertex joints then index - * into the array offset by @ref PhongDrawUniform::jointOffset. If - * @p count is @cpp 0 @ce, skinning is not performed. + * If @ref Flag::UniformBuffers isn't set, @p count describes an upper + * bound on how many joint matrices get supplied to each draw with + * @ref setJointMatrices() / @ref setJointMatrix(). If + * @ref Flag::UniformBuffers is set, @p count describes size of a + * @ref TransformationUniform3D buffer bound with @ref bindJointBuffer(); + * as uniform buffers are required to have a statically defined size. + * The per-vertex joints then index into the array offset by + * @ref PhongDrawUniform::jointOffset. If @p count is @cpp 0 @ce, + * skinning is not performed. * * The @p perVertexCount and @p secondaryPerVertexCount then describe * how many components are taken from @ref JointIds / @ref Weights and diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index f13f0ff93..1747021cb 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -542,7 +542,12 @@ const struct { {3*4, FlatGL2D::Weights{FlatGL2D::Weights::Components::Three}}, }}, false, true, false, "skinning.tga"}, - {"single set, joint matrices one by one", 5, 3, 0, 0, 0, {}, {InPlaceInit, { + {"single set, upload just a prefix of joint matrices", 15, 3, 0, 0, 0, {}, {InPlaceInit, { + {0, FlatGL2D::JointIds{FlatGL2D::JointIds::Components::Three}}, + {3*4, FlatGL2D::Weights{FlatGL2D::Weights::Components::Three}}, + }}, false, true, false, + "skinning.tga"}, + {"single set, upload joint matrices one by one", 5, 3, 0, 0, 0, {}, {InPlaceInit, { {0, FlatGL2D::JointIds{FlatGL2D::JointIds::Components::Three}}, {3*4, FlatGL2D::Weights{FlatGL2D::Weights::Components::Three}}, }}, false, true, true, @@ -1587,10 +1592,12 @@ template void FlatGLTest::setWrongJointCountOrId() { std::ostringstream out; Error redirectError{&out}; - shader.setJointMatrices({MatrixTypeFor{}}); - shader.setJointMatrix(5, MatrixTypeFor{}); + /* Calling setJointMatrices() with less items is fine, tested in + renderSkinning*D() */ + shader.setJointMatrices({{}, {}, {}, {}, {}, {}}) + .setJointMatrix(5, MatrixTypeFor{}); CORRADE_COMPARE(out.str(), - "Shaders::FlatGL::setJointMatrices(): expected 5 items but got 1\n" + "Shaders::FlatGL::setJointMatrices(): expected at most 5 items but got 6\n" "Shaders::FlatGL::setJointMatrix(): joint ID 5 is out of bounds for 5 joints\n"); } #endif diff --git a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp index 416ee08f9..e8de58647 100644 --- a/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp +++ b/src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp @@ -935,7 +935,12 @@ const struct { {3*4, MeshVisualizerGL2D::Weights{MeshVisualizerGL2D::Weights::Components::Three}}, }}, false, true, false, "skinning.tga"}, - {"single set, joint matrices one by one", 5, 3, 0, 0, 0, {}, {}, {InPlaceInit, { + {"single set, upload just a prefix of joint matrices", 15, 3, 0, 0, 0, {}, {}, {InPlaceInit, { + {0, MeshVisualizerGL2D::JointIds{MeshVisualizerGL2D::JointIds::Components::Three}}, + {3*4, MeshVisualizerGL2D::Weights{MeshVisualizerGL2D::Weights::Components::Three}}, + }}, false, true, false, + "skinning.tga"}, + {"single set, upload joint matrices one by one", 5, 3, 0, 0, 0, {}, {}, {InPlaceInit, { {0, MeshVisualizerGL2D::JointIds{MeshVisualizerGL2D::JointIds::Components::Three}}, {3*4, MeshVisualizerGL2D::Weights{MeshVisualizerGL2D::Weights::Components::Three}}, }}, false, true, true, @@ -2881,10 +2886,12 @@ void MeshVisualizerGLTest::setWrongJointCountOrId2D() { std::ostringstream out; Error redirectError{&out}; - shader.setJointMatrices({Matrix3{}}); - shader.setJointMatrix(5, Matrix3{}); + /* Calling setJointMatrices() with less items is fine, tested in + renderSkinningWireframe2D() */ + shader.setJointMatrices({{}, {}, {}, {}, {}, {}}) + .setJointMatrix(5, Matrix3{}); CORRADE_COMPARE(out.str(), - "Shaders::MeshVisualizerGL2D::setJointMatrices(): expected 5 items but got 1\n" + "Shaders::MeshVisualizerGL2D::setJointMatrices(): expected at most 5 items but got 6\n" "Shaders::MeshVisualizerGL2D::setJointMatrix(): joint ID 5 is out of bounds for 5 joints\n"); } @@ -2904,10 +2911,12 @@ void MeshVisualizerGLTest::setWrongJointCountOrId3D() { std::ostringstream out; Error redirectError{&out}; - shader.setJointMatrices({Matrix4{}}); - shader.setJointMatrix(5, Matrix4{}); + /* Calling setJointMatrices() with less items is fine, tested in + renderSkinningWireframe3D() */ + shader.setJointMatrices({{}, {}, {}, {}, {}, {}}) + .setJointMatrix(5, Matrix4{}); CORRADE_COMPARE(out.str(), - "Shaders::MeshVisualizerGL3D::setJointMatrices(): expected 5 items but got 1\n" + "Shaders::MeshVisualizerGL3D::setJointMatrices(): expected at most 5 items but got 6\n" "Shaders::MeshVisualizerGL3D::setJointMatrix(): joint ID 5 is out of bounds for 5 joints\n"); } #endif diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index a213d5071..54f4142f6 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -857,7 +857,12 @@ const struct { {3*4, PhongGL::Weights{PhongGL::Weights::Components::Three}}, }}, false, true, false, "skinning.tga"}, - {"single set, joint matrices one by one", 5, 3, 0, 0, 0, {}, {InPlaceInit, { + {"single set, upload just a prefix of joint matrices", 15, 3, 0, 0, 0, {}, {InPlaceInit, { + {0, PhongGL::JointIds{PhongGL::JointIds::Components::Three}}, + {3*4, PhongGL::Weights{PhongGL::Weights::Components::Three}}, + }}, false, true, false, + "skinning.tga"}, + {"single set, upload joint matrices one by one", 5, 3, 0, 0, 0, {}, {InPlaceInit, { {0, PhongGL::JointIds{PhongGL::JointIds::Components::Three}}, {3*4, PhongGL::Weights{PhongGL::Weights::Components::Three}}, }}, false, true, true, @@ -2008,10 +2013,12 @@ void PhongGLTest::setWrongJointCountOrId() { std::ostringstream out; Error redirectError{&out}; - shader.setJointMatrices({Matrix4{}}) + /* Calling setJointMatrices() with less items is fine, tested in + renderSkinning() */ + shader.setJointMatrices({{}, {}, {}, {}, {}, {}}) .setJointMatrix(5, Matrix4{}); CORRADE_COMPARE(out.str(), - "Shaders::PhongGL::setJointMatrices(): expected 5 items but got 1\n" + "Shaders::PhongGL::setJointMatrices(): expected at most 5 items but got 6\n" "Shaders::PhongGL::setJointMatrix(): joint ID 5 is out of bounds for 5 joints\n"); } #endif