Performance pass to Cloth CPU Skinning

- Added operator+(Matrix3x4), operator*(float), RetrieveScaleSq and GetReciprocalScaled to Matrix3x4. Used by Cloth CPU Linear Skinning. These operation will be performant as they use SIMD.
- Modified so there are no virtual functions calls at vertex level.
- Caching indices to simplify the loop when applying skinning.
- Caching static variables Matrix3x4 zero and DualQuaternion zero to avoid creating it for every vertex.
- Removing branching to skip joints when the weight is zero, these cases are rarely and this improves performance by removing branching from loops at vertex level.
- Changing skinning influences so it's a continuous block of memory.
- Caching the vector size() if a variable instead of directly using it in a for loop.
This commit is contained in:
Aaron Ruiz Mora
2021-05-04 12:09:56 +01:00
committed by GitHub
parent 83545c0243
commit 70bd3ea0ff
7 changed files with 388 additions and 210 deletions
@@ -231,6 +231,18 @@ namespace AZ
//! Compound assignment operator for matrix-matrix multiplication.
Matrix3x4& operator*=(const Matrix3x4& rhs);
//! Operator for matrix-matrix addition.
[[nodiscard]] Matrix3x4 operator+(const Matrix3x4& rhs) const;
//! Compound assignment operator for matrix-matrix addition.
Matrix3x4& operator+=(const Matrix3x4& rhs);
//! Operator for multiplying all matrix's elements with a scalar
[[nodiscard]] Matrix3x4 operator*(float scalar) const;
//! Compound assignment operator for multiplying all matrix's elements with a scalar
Matrix3x4& operator*=(float scalar);
//! Operator for transforming a Vector3.
[[nodiscard]] Vector3 operator*(const Vector3& rhs) const;
@@ -274,12 +286,18 @@ namespace AZ
//! Gets the scale part of the transformation (the length of the basis vectors).
[[nodiscard]] Vector3 RetrieveScale() const;
//! Gets the squared scale part of the transformation (the squared length of the basis vectors).
[[nodiscard]] Vector3 RetrieveScaleSq() const;
//! Gets the scale part of the transformation as in RetrieveScale, and also removes this scaling from the matrix.
Vector3 ExtractScale();
//! Multiplies the basis vectors of the matrix by the elements of the scale specified.
void MultiplyByScale(const Vector3& scale);
//! Returns a matrix with the reciprocal scale, keeping the same rotation and translation.
[[nodiscard]] Matrix3x4 GetReciprocalScaled() const;
//! Tests if the 3x3 part of the matrix is orthogonal.
bool IsOrthogonal(float tolerance = Constants::Tolerance) const;
@@ -487,6 +487,43 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator+(const Matrix3x4& rhs) const
{
return Matrix3x4
(
Simd::Vec4::Add(m_rows[0].GetSimdValue(), rhs.m_rows[0].GetSimdValue()),
Simd::Vec4::Add(m_rows[1].GetSimdValue(), rhs.m_rows[1].GetSimdValue()),
Simd::Vec4::Add(m_rows[2].GetSimdValue(), rhs.m_rows[2].GetSimdValue())
);
}
AZ_MATH_INLINE Matrix3x4& Matrix3x4::operator+=(const Matrix3x4& rhs)
{
*this = *this + rhs;
return *this;
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::operator*(float scalar) const
{
const Vector4 vector4Scalar(scalar);
return Matrix3x4
(
Simd::Vec4::Mul(m_rows[0].GetSimdValue(), vector4Scalar.GetSimdValue()),
Simd::Vec4::Mul(m_rows[1].GetSimdValue(), vector4Scalar.GetSimdValue()),
Simd::Vec4::Mul(m_rows[2].GetSimdValue(), vector4Scalar.GetSimdValue())
);
}
AZ_MATH_INLINE Matrix3x4& Matrix3x4::operator*=(float scalar)
{
*this = *this * scalar;
return *this;
}
AZ_MATH_INLINE Vector3 Matrix3x4::operator*(const Vector3& rhs) const
{
return Vector3
@@ -583,6 +620,12 @@ namespace AZ
}
AZ_MATH_INLINE Vector3 Matrix3x4::RetrieveScaleSq() const
{
return Vector3(GetColumn(0).GetLengthSq(), GetColumn(1).GetLengthSq(), GetColumn(2).GetLengthSq());
}
AZ_MATH_INLINE Vector3 Matrix3x4::ExtractScale()
{
const Vector3 scale = RetrieveScale();
@@ -600,6 +643,14 @@ namespace AZ
}
AZ_MATH_INLINE Matrix3x4 Matrix3x4::GetReciprocalScaled() const
{
Matrix3x4 result = *this;
result.MultiplyByScale(RetrieveScaleSq().GetReciprocal());
return result;
}
AZ_MATH_INLINE void Matrix3x4::Orthogonalize()
{
*this = GetOrthogonalized();
@@ -484,6 +484,38 @@ namespace UnitTest
EXPECT_TRUE(matrix5.IsClose(matrix1 * matrix4));
}
TEST(MATH_Matrix3x4, AddMatrix3x4)
{
const AZ::Matrix3x4 matrix1 = AZ::Matrix3x4::CreateFromValue(1.2f);
const AZ::Matrix3x4 matrix2 = AZ::Matrix3x4::CreateDiagonal(AZ::Vector3(1.3f, 1.5f, 0.4f));
const AZ::Matrix3x4 matrix3 = AZ::Matrix3x4::CreateFromQuaternionAndTranslation(
AZ::Quaternion(0.42f, 0.46f, -0.66f, 0.42f), AZ::Vector3(2.8f, -3.7f, 1.6f));
const AZ::Matrix3x4 matrix4 = AZ::Matrix3x4::CreateRotationX(-0.7f) * AZ::Matrix3x4::CreateScale(AZ::Vector3(0.6f, 1.3f, 0.7f));
AZ::Matrix3x4 matrix5 = matrix1;
matrix5 += matrix4;
EXPECT_THAT(matrix1 + (matrix2 + matrix3), IsClose((matrix1 + matrix2) + matrix3));
EXPECT_THAT(matrix2 + AZ::Matrix3x4::CreateZero(), IsClose(matrix2));
EXPECT_THAT(matrix3 + AZ::Matrix3x4::CreateZero(), IsClose(AZ::Matrix3x4::CreateZero() + matrix3));
EXPECT_THAT(matrix3 + matrix3, IsClose(matrix3 * 2.0f));
EXPECT_THAT(matrix5, IsClose(matrix1 + matrix4));
}
TEST(MATH_Matrix3x4, MultiplyByScalar)
{
const AZ::Vector4 row0(1.488f, 2.56f, 0.096f, 2.3f);
const AZ::Vector4 row1(0.384f, -1.92f, 0.428f, -1.6f);
const AZ::Vector4 row2(1.28f, -2.4f, -0.24f, 3.7f);
const float scalar = 3.2f;
const AZ::Vector4 row0Result = row0 * scalar;
const AZ::Vector4 row1Result = row1 * scalar;
const AZ::Vector4 row2Result = row2 * scalar;
AZ::Matrix3x4 matrix = AZ::Matrix3x4::CreateFromRows(row0, row1, row2);
EXPECT_THAT(matrix * 0.0f, IsClose(AZ::Matrix3x4::CreateZero()));
EXPECT_THAT(matrix * 1.0f, IsClose(matrix));
EXPECT_THAT(matrix * scalar, IsClose(AZ::Matrix3x4::CreateFromRows(row0Result, row1Result, row2Result)));
EXPECT_THAT(matrix * 2.0f, IsClose(matrix + matrix));
}
TEST(MATH_Matrix3x4, MultiplyByVector3)
{
const AZ::Vector4 row0(1.488f, 2.56f, 0.096f, 2.3f);
@@ -652,6 +684,34 @@ namespace UnitTest
EXPECT_THAT(scaledMatrix.RetrieveScale(), IsClose(AZ::Vector3::CreateOne()));
}
TEST_P(Matrix3x4ScaleFixture, ScaleSq)
{
const AZ::Matrix3x4 orthogonalMatrix = GetParam();
EXPECT_THAT(orthogonalMatrix.RetrieveScaleSq(), IsClose(AZ::Vector3::CreateOne()));
AZ::Matrix3x4 unscaledMatrix = orthogonalMatrix;
unscaledMatrix.ExtractScale();
EXPECT_THAT(unscaledMatrix.RetrieveScaleSq(), IsClose(AZ::Vector3::CreateOne()));
const AZ::Vector3 scale(2.8f, 0.7f, 1.3f);
AZ::Matrix3x4 scaledMatrix = orthogonalMatrix;
scaledMatrix.MultiplyByScale(scale);
EXPECT_THAT(scaledMatrix.RetrieveScaleSq(), IsClose(scale * scale));
EXPECT_THAT(scaledMatrix.RetrieveScaleSq(), IsClose(scaledMatrix.RetrieveScale() * scaledMatrix.RetrieveScale()));
scaledMatrix.ExtractScale();
EXPECT_THAT(scaledMatrix.RetrieveScaleSq(), IsClose(AZ::Vector3::CreateOne()));
}
TEST_P(Matrix3x4ScaleFixture, GetReciprocalScaled)
{
const AZ::Matrix3x4 orthogonalMatrix = GetParam();
EXPECT_THAT(orthogonalMatrix.GetReciprocalScaled(), IsClose(orthogonalMatrix));
const AZ::Vector3 scale(2.8f, 0.7f, 1.3f);
AZ::Matrix3x4 scaledMatrix = orthogonalMatrix;
scaledMatrix.MultiplyByScale(scale);
AZ::Matrix3x4 reciprocalScaledMatrix = orthogonalMatrix;
reciprocalScaledMatrix.MultiplyByScale(scale.GetReciprocal());
EXPECT_THAT(scaledMatrix.GetReciprocalScaled(), IsClose(reciprocalScaledMatrix));
}
INSTANTIATE_TEST_CASE_P(MATH_Matrix3x4, Matrix3x4ScaleFixture, ::testing::ValuesIn(MathTestData::OrthogonalMatrix3x4s));
TEST(MATH_Matrix3x4, IsOrthogonal)