blob: 59a5283e511fff0f82b9a37256ff04c9770bba9b [file] [log] [blame]
// Copyright 2026 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <gtest/gtest.h>
#include "dawn/common/Algebra.h"
namespace dawn::math {
namespace {
// Test that the algebra types have the same size and alignment as in WGSL.
// See https://gpuweb.github.io/gpuweb/wgsl/#alignment-and-size
static_assert(sizeof(Vec2f) == 8);
static_assert(sizeof(Vec3f) == 16); // 12 in WGSL but in C++ sizeof is rounded to the alignment.
static_assert(sizeof(Vec4f) == 16);
static_assert(sizeof(Vec2u) == 8);
static_assert(sizeof(Vec3u) == 16); // Same
static_assert(sizeof(Vec4u) == 16);
static_assert(sizeof(Vec2i) == 8);
static_assert(sizeof(Vec3i) == 16); // Same
static_assert(sizeof(Vec4i) == 16);
static_assert(sizeof(Mat2x2f) == 16);
static_assert(sizeof(Mat3x2f) == 24);
static_assert(sizeof(Mat4x2f) == 32);
static_assert(sizeof(Mat2x3f) == 32);
static_assert(sizeof(Mat3x3f) == 48);
static_assert(sizeof(Mat4x3f) == 64);
static_assert(sizeof(Mat2x4f) == 32);
static_assert(sizeof(Mat3x4f) == 48);
static_assert(sizeof(Mat4x4f) == 64);
static_assert(alignof(Vec2f) == 8);
static_assert(alignof(Vec3f) == 16);
static_assert(alignof(Vec4f) == 16);
static_assert(alignof(Vec2u) == 8);
static_assert(alignof(Vec3u) == 16);
static_assert(alignof(Vec4u) == 16);
static_assert(alignof(Vec2i) == 8);
static_assert(alignof(Vec3i) == 16);
static_assert(alignof(Vec4i) == 16);
static_assert(alignof(Mat2x2f) == 8);
static_assert(alignof(Mat3x2f) == 8);
static_assert(alignof(Mat4x2f) == 8);
static_assert(alignof(Mat2x3f) == 16);
static_assert(alignof(Mat3x3f) == 16);
static_assert(alignof(Mat4x3f) == 16);
static_assert(alignof(Mat2x4f) == 16);
static_assert(alignof(Mat3x4f) == 16);
static_assert(alignof(Mat4x4f) == 16);
// Check that the vector constructor set components as expected (and implicitly test the indexing
// operators)
TEST(Algebra, VectorConstructorAndIndexing) {
// Check the Vector constructor from an array.
{
auto a = Vec2u{{1, 2}};
auto b = Vec3i{{3, 4, 5}};
auto c = Vec4f{{6, 7, 8, 9}};
EXPECT_EQ(a[0], 1u);
EXPECT_EQ(a[1], 2u);
EXPECT_EQ(b[0], 3);
EXPECT_EQ(b[1], 4);
EXPECT_EQ(b[2], 5);
EXPECT_EQ(c[0], 6.0f);
EXPECT_EQ(c[1], 7.0f);
EXPECT_EQ(c[2], 8.0f);
EXPECT_EQ(c[3], 9.0f);
}
// Check the Vector constructor from an individual scalars.
{
auto a = Vec2u{1, 2};
auto b = Vec3i{3, 4, 5};
auto c = Vec4f{6, 7, 8, 9};
EXPECT_EQ(a[0], 1u);
EXPECT_EQ(a[1], 2u);
EXPECT_EQ(b[0], 3);
EXPECT_EQ(b[1], 4);
EXPECT_EQ(b[2], 5);
EXPECT_EQ(c[0], 6.0f);
EXPECT_EQ(c[1], 7.0f);
EXPECT_EQ(c[2], 8.0f);
EXPECT_EQ(c[3], 9.0f);
// Test setting with the indexing operator.
a[1] = 0;
EXPECT_EQ(a, Vec2u(1, 0));
}
}
// Test casting between vectors of different scalar types.
TEST(Algebra, VectorConstructorFromVectorWithDifferentScalarType) {
EXPECT_EQ(Vec2u(Vec2f(3.0, 4.0)), Vec2u(3, 4));
EXPECT_EQ(Vec4f(Vec4i(-1, 3, 4, -4)), Vec4f(-1.0, 3.0, 4.0, -4.0));
}
// Test the vector equality operator
TEST(Algebra, VectorEquality) {
EXPECT_EQ(Vec4u(1, 2, 3, 4), Vec4u(1, 2, 3, 4));
EXPECT_NE(Vec4u(1, 2, 3, 4), Vec4u(0, 2, 3, 4));
EXPECT_NE(Vec4u(1, 2, 3, 4), Vec4u(1, 0, 3, 4));
EXPECT_NE(Vec4u(1, 2, 3, 4), Vec4u(1, 2, 0, 4));
EXPECT_NE(Vec4u(1, 2, 3, 4), Vec4u(1, 2, 3, 0));
}
// Test the vector-vector addition.
TEST(Algebra, VectorVectorAdd) {
EXPECT_EQ(Vec3f(1, 2, 3) + Vec3f(10, 11, 12), Vec3f(11, 13, 15));
Vec3f accumulator = Vec3f(1, 2, 3);
accumulator += Vec3f(10, 11, 12);
EXPECT_EQ(accumulator, Vec3f(11, 13, 15));
}
// Test the vector-vector subtraction.
TEST(Algebra, VectorVectorSub) {
EXPECT_EQ(Vec3f(10, 11, 12) - Vec3f(1, 2, 3), Vec3f(9, 9, 9));
Vec3f accumulator = Vec3f(1, 2, 3);
accumulator -= Vec3f(10, 11, 12);
EXPECT_EQ(accumulator, Vec3f(-9, -9, -9));
}
// Test the vector-vector multiplication.
TEST(Algebra, VectorVectorMul) {
EXPECT_EQ(Vec3f(1, 2, 3) * Vec3f(-4, 3, 5), Vec3f(-4, 6, 15));
Vec3f accumulator = Vec3f(1, 2, 3);
accumulator *= Vec3f(-4, 3, 5);
EXPECT_EQ(accumulator, Vec3f(-4, 6, 15));
}
// Test the vector-vector division.
TEST(Algebra, VectorVectorDiv) {
EXPECT_EQ(Vec3f(8, 6, 15) / Vec3f(2, 3, 5), Vec3f(4, 2, 3));
Vec3f accumulator = Vec3f(8, 6, 15);
accumulator /= Vec3f(2, 3, 5);
EXPECT_EQ(accumulator, Vec3f(4, 2, 3));
}
// Test the vector-scalar multiplication.
TEST(Algebra, VectorScalarMul) {
EXPECT_EQ(Vec3f(1, 2, 3) * 3.0f, Vec3f(3, 6, 9));
EXPECT_EQ(3.0f * Vec3f(1, 2, 3), Vec3f(3, 6, 9));
Vec3f a = Vec3f(1, 2, 3);
a *= 3.0f;
EXPECT_EQ(a, Vec3f(3, 6, 9));
}
// Check that the vector constructor set columns as expected (and implicitly test the indexing
// operators)
TEST(Algebra, MatrixConstructorAndIndexing) {
// Check the Matrix constructor from an array.
{
auto a = Mat2x3f{{Vec3f(1, 2, 3), Vec3f(4, 5, 6)}};
EXPECT_EQ(a[0], Vec3f(1, 2, 3));
EXPECT_EQ(a[1], Vec3f(4, 5, 6));
}
// Check the Matrix constructor from a individual columns.
{
auto a = Mat2x3f{Vec3f(1, 2, 3), Vec3f(4, 5, 6)};
EXPECT_EQ(a[0], Vec3f(1, 2, 3));
EXPECT_EQ(a[1], Vec3f(4, 5, 6));
// Test setting with the indexing operator.
a[1] = Vec3f(0, 0, 0);
EXPECT_EQ(a, Mat2x3f({{1, 2, 3}, {0, 0, 0}}));
}
}
// Test the vector equality operator
TEST(Algebra, MatrixEquality) {
EXPECT_EQ(Mat3x2f({1, 2}, {3, 4}, {5, 6}), Mat3x2f({1, 2}, {3, 4}, {5, 6}));
EXPECT_NE(Mat3x2f({1, 2}, {3, 4}, {5, 6}), Mat3x2f({0, 2}, {3, 4}, {5, 6}));
EXPECT_NE(Mat3x2f({1, 2}, {3, 4}, {5, 6}), Mat3x2f({1, 0}, {3, 4}, {5, 6}));
EXPECT_NE(Mat3x2f({1, 2}, {3, 4}, {5, 6}), Mat3x2f({1, 2}, {0, 4}, {5, 6}));
EXPECT_NE(Mat3x2f({1, 2}, {3, 4}, {5, 6}), Mat3x2f({1, 2}, {3, 0}, {5, 6}));
EXPECT_NE(Mat3x2f({1, 2}, {3, 4}, {5, 6}), Mat3x2f({1, 2}, {3, 4}, {0, 6}));
EXPECT_NE(Mat3x2f({1, 2}, {3, 4}, {5, 6}), Mat3x2f({1, 2}, {3, 4}, {5, 0}));
}
// Test that identity matrices are diagonals filled with 1s
TEST(Algebra, MatrixIdentity) {
EXPECT_EQ(Mat2x2f::Identity(), Mat2x2f({1, 0}, {0, 1}));
EXPECT_EQ(Mat3x3f::Identity(), Mat3x3f({1, 0, 0}, {0, 1, 0}, {0, 0, 1}));
EXPECT_EQ(Mat4x4f::Identity(), Mat4x4f({1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}));
}
// Test the creation of scale matrices
TEST(Algebra, MatrixScale) {
EXPECT_EQ(Mat2x2f::Scale({1, 2}), Mat2x2f({1, 0}, {0, 2}));
EXPECT_EQ(Mat3x3f::Scale({3, 4, 5}), Mat3x3f({3, 0, 0}, {0, 4, 0}, {0, 0, 5}));
EXPECT_EQ(Mat4x4f::Scale({6, 7, 8, 9}),
Mat4x4f({6, 0, 0, 0}, {0, 7, 0, 0}, {0, 0, 8, 0}, {0, 0, 0, 9}));
}
// Test the creation of scale matrices
TEST(Algebra, MatrixTranslation) {
EXPECT_EQ(Mat3x3f::Translation({3, 4}), Mat3x3f({1, 0, 0}, {0, 1, 0}, {3, 4, 1}));
EXPECT_EQ(Mat4x4f::Translation({6, 7, 8}),
Mat4x4f({1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {6, 7, 8, 1}));
}
// Test the creation of homogeneous space scale matrices
TEST(Algebra, MatrixScaleHomogeneous) {
EXPECT_EQ(Mat3x3f::ScaleHomogeneous({3, 4}), Mat3x3f({3, 0, 0}, {0, 4, 0}, {0, 0, 1}));
EXPECT_EQ(Mat4x4f::ScaleHomogeneous({6, 7, 8}),
Mat4x4f({6, 0, 0, 0}, {0, 7, 0, 0}, {0, 0, 8, 0}, {0, 0, 0, 1}));
}
// Test casting between matrices of different dimension (it crops or fills with zeroes).
TEST(Algebra, MatrixCropOrExpandFrom) {
EXPECT_EQ(Mat4x2f::CropOrExpandFrom(Mat2x4f({1, 2, 3, 4}, {5, 6, 7, 8})),
Mat4x2f({1, 2}, {5, 6}, {0, 0}, {0, 0}));
}
// Test matrix / vector multiplication.
TEST(Algebra, MatrixVectorMul) {
auto m = Mat3x4f({1, 2, 3, 4}, {-1, -2, -3, -4}, {10, 11, 12, 13});
// The matrix transforms basis vectors to column vectors.
EXPECT_EQ(Mul(m, Vec3f(1, 0, 0)), m[0]);
EXPECT_EQ(Mul(m, Vec3f(0, 1, 0)), m[1]);
EXPECT_EQ(Mul(m, Vec3f(0, 0, 1)), m[2]);
EXPECT_EQ(Mul(m, Vec3f(1, 2, 3)), m[0] * 1 + m[1] * 2 + m[2] * 3);
}
// Test matrix / matrix multiplication.
TEST(Algebra, MatrixMatrixMul) {
// Take two matrices that can be multiplied together, containing arbitrary values that are not
// special wrt to matrix multiplication.
auto A = Mat4x3f({10, 11, 12}, {4, 3, 2}, {3, 5, 7}, {-2, -5, -8});
auto B = Mat3x4f({1, 2, 3, 4}, {-1, -2, -3, -4}, {10, 11, 12, 13});
// The resulting matrix is defined by the property that multiplying vectors with it is the same
// as multiplying the vector with B and then A. Check each basis vector.
auto R = Mul(A, B);
EXPECT_EQ(Mul(R, Vec3f(1, 0, 0)), Mul(A, Mul(B, Vec3f(1, 0, 0))));
EXPECT_EQ(Mul(R, Vec3f(0, 1, 0)), Mul(A, Mul(B, Vec3f(0, 1, 0))));
EXPECT_EQ(Mul(R, Vec3f(0, 0, 1)), Mul(A, Mul(B, Vec3f(0, 0, 1))));
}
// Test vector / vector max.
TEST(Algebra, VectorVectorMax) {
EXPECT_EQ(Max(Vec2f(2, 7), Vec2f(6, 3)), Vec2f(6, 7));
EXPECT_EQ(Max(Vec4u(1, 2, 3, 4), Vec4u(4, 3, 2, 1)), Vec4u(4, 3, 3, 4));
}
} // anonymous namespace
} // namespace dawn::math