From 8ac06890b7a99de5fb11be54ae3883928528893c Mon Sep 17 00:00:00 2001 From: Myles Busig Date: Fri, 30 Aug 2024 15:29:51 -0700 Subject: [PATCH] Rewrite math vector implementation and add tests This new implementation consolidates vec2/3/4 into one base class and improves performance. --- include/mtl/tests/vec.hpp | 100 +++++++++++++++ include/mtl/vec.hpp | 163 ++++++++++++++++++++++++ include/mtl/vec4.hpp | 34 ----- src/tests/vec.cpp | 261 ++++++++++++++++++++++++++++++++++++++ src/vec4.cpp | 29 ----- 5 files changed, 524 insertions(+), 63 deletions(-) create mode 100644 include/mtl/tests/vec.hpp create mode 100644 include/mtl/vec.hpp delete mode 100644 include/mtl/vec4.hpp create mode 100644 src/tests/vec.cpp delete mode 100644 src/vec4.cpp diff --git a/include/mtl/tests/vec.hpp b/include/mtl/tests/vec.hpp new file mode 100644 index 0000000..0d50a27 --- /dev/null +++ b/include/mtl/tests/vec.hpp @@ -0,0 +1,100 @@ +#pragma once + +#include "mtl/test.hpp" + +#include "mtl/vec.hpp" + +namespace mtl { + +namespace test { + +class vec_suite : public suite { +public: + vec_suite() { + add_test(&construction_v2, "construction"); + add_test(&construction_v3, "construction"); + add_test(&construction_v4, "construction"); + + add_test(&addition_v2, "addition_v2"); + add_test(&addition_v3, "addition_v3"); + add_test(&addition_v4, "addition_v4"); + add_test(&subtraction_v2, "subtraction_v2"); + add_test(&subtraction_v3, "subtraction_v3"); + add_test(&subtraction_v4, "subtraction_v4"); + add_test(&negation_v2, "negation_v2"); + add_test(&negation_v3, "negation_v3"); + add_test(&negation_v4, "negation_v4"); + add_test(&mult_scalar_v2, "mult_scalar_v2"); + add_test(&mult_scalar_v3, "mult_scalar_v3"); + add_test(&mult_scalar_v4, "mult_scalar_v4"); + add_test(&dot_v2, "dot_v2"); + add_test(&dot_v3, "dot_v3"); + add_test(&dot_v4, "dot_v4"); + add_test(&division_scalar_v2, "division_scalar_v2"); + add_test(&division_scalar_v3, "division_scalar_v3"); + add_test(&division_scalar_v4, "division_scalar_v4"); + add_test(&magnitude_sqr_v2, "magnitude_sqr_v2"); + add_test(&magnitude_sqr_v3, "magnitude_sqr_v3"); + add_test(&magnitude_sqr_v4, "magnitude_sqr_v4"); + add_test(&transpose_v2, "transpose_v2"); + add_test(&transpose_v3, "transpose_v3"); + add_test(&transpose_v4, "transpose_v4"); + } + + virtual string_view name() { + return "vec_suite"; + } + + static bool construction_v2() { + vec2 a(1, 2); + + return a.x == 1 && a.y == 2; + } + static bool construction_v3() { + vec3 a(1, 2, 3); + + return a.x == 1 && a.y == 2 && a.z == 3; + } + static bool construction_v4() { + vec4 a(1, 2, 3, 4); + + return a.x == 1 && a.y == 2 && a.z == 3 && a.w == 4; + } + + GBA_IWRAM ARM_MODE static bool addition_v2(); + GBA_IWRAM ARM_MODE static bool addition_v3(); + GBA_IWRAM ARM_MODE static bool addition_v4(); + + GBA_IWRAM ARM_MODE static bool subtraction_v2(); + GBA_IWRAM ARM_MODE static bool subtraction_v3(); + GBA_IWRAM ARM_MODE static bool subtraction_v4(); + + GBA_IWRAM ARM_MODE static bool negation_v2(); + GBA_IWRAM ARM_MODE static bool negation_v3(); + GBA_IWRAM ARM_MODE static bool negation_v4(); + + GBA_IWRAM ARM_MODE static bool mult_scalar_v2(); + GBA_IWRAM ARM_MODE static bool mult_scalar_v3(); + GBA_IWRAM ARM_MODE static bool mult_scalar_v4(); + + GBA_IWRAM ARM_MODE static bool dot_v2(); + GBA_IWRAM ARM_MODE static bool dot_v3(); + GBA_IWRAM ARM_MODE static bool dot_v4(); + + GBA_IWRAM ARM_MODE static bool division_scalar_v2(); + GBA_IWRAM ARM_MODE static bool division_scalar_v3(); + GBA_IWRAM ARM_MODE static bool division_scalar_v4(); + + GBA_IWRAM ARM_MODE static bool magnitude_sqr_v2(); + GBA_IWRAM ARM_MODE static bool magnitude_sqr_v3(); + GBA_IWRAM ARM_MODE static bool magnitude_sqr_v4(); + + GBA_IWRAM ARM_MODE static bool transpose_v2(); + GBA_IWRAM ARM_MODE static bool transpose_v3(); + GBA_IWRAM ARM_MODE static bool transpose_v4(); +}; + +} // namespace test + +} // namespace mtl + diff --git a/include/mtl/vec.hpp b/include/mtl/vec.hpp new file mode 100644 index 0000000..d499fdb --- /dev/null +++ b/include/mtl/vec.hpp @@ -0,0 +1,163 @@ +#pragma once + +#include "mtl/target.hpp" + +#include + +#include "mtl/fixed.hpp" + +namespace mtl { + +template +class mat; + +template +class vec { +public: + fixed e[N]; + + constexpr vec() noexcept {} + + constexpr vec(const vec& other) noexcept { + // We need to explicitly define the copy constructor, otherwise + // GCC uses memcpy to copy while in Thumb mode, and that's slow. + for (size_t i = 0; i < N; ++i) { + e[i] = other.e[i]; + } + } + constexpr vec(const fixed (&_e)[N]) noexcept { + for (size_t i = 0; i < N; ++i) { + e[i] = _e[i]; + } + } + + constexpr fixed& operator[](size_t i) noexcept { + return e[i]; + } + constexpr const fixed& operator[](size_t i) const noexcept { + return e[i]; + } + + vec operator+(const vec& rhs) const noexcept { + vec res; + + for (size_t i = 0; i < N; ++i) { + res[i] = e[i] + rhs[i]; + } + + return res; + } + + vec operator-(const vec& rhs) const noexcept { + vec res; + + for (size_t i = 0; i < N; ++i) { + res[i] = e[i] - rhs[i]; + } + + return res; + } + + vec operator-() const noexcept { + vec res; + + for (size_t i = 0; i < N; ++i) { + res[i] = -e[i]; + } + + return res; + } + + vec operator*(fixed rhs) const noexcept { + vec res; + + for (size_t i = 0; i < N; ++i) { + res[i] = e[i] * rhs; + } + + return res; + } + friend vec operator*(fixed lhs, vec rhs) noexcept { + return rhs * lhs; + } + + fixed operator*(const vec& rhs) const noexcept { + fixed res; + + for (size_t i = 0; i < N; ++i) { + res += e[i] * rhs[i]; + } + + return res; + } + + vec operator/(fixed rhs) const noexcept { + vec r; + + for (size_t i = 0; i < N; ++i) { + r[i] = e[i] / rhs; + } + + return r; + } + + fixed magnitude_sqr() const noexcept { + fixed r; + + for (size_t i = 0; i < N; ++i) { + r += e[i] * e[i]; + } + + return r; + } + + mat<1, N> transpose() const noexcept; +}; + +class vec2 : public vec<2> { +public: + fixed& x = e[0]; + fixed& y = e[1]; + + constexpr vec2() noexcept {} + constexpr vec2(const vec<2>& other) noexcept : vec(other) {} + constexpr vec2(fixed _x, fixed _y) noexcept { + x = _x; + y = _y; + } +}; + +class vec3 : public vec<3> { +public: + fixed& x = e[0]; + fixed& y = e[1]; + fixed& z = e[2]; + + constexpr vec3() noexcept {} + constexpr vec3(const vec<3>& other) noexcept : vec(other) {} + constexpr vec3(fixed _x, fixed _y, fixed _z) noexcept { + x = _x; + y = _y; + z = _z; + } +}; + +class vec4 : public vec<4> { +public: + fixed& x = e[0]; + fixed& y = e[1]; + fixed& z = e[2]; + fixed& w = e[3]; + + constexpr vec4() noexcept {} + constexpr vec4(const vec<4>& other) noexcept : vec(other) {} + constexpr vec4(fixed _x, fixed _y, fixed _z, fixed _w) noexcept { + x = _x; + y = _y; + z = _z; + w = _w; + } +}; + +} // namespace mtl + diff --git a/include/mtl/vec4.hpp b/include/mtl/vec4.hpp deleted file mode 100644 index d6c4d5c..0000000 --- a/include/mtl/vec4.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "mtl/target.hpp" - -#include "mtl/fixed.hpp" - -TARGET_ARM_MODE - -namespace mtl { - -class vec4 { -public: - fixed x, y, z, w; - - constexpr vec4(fixed _x = 0, fixed _y = 0, fixed _z = 0, fixed _w = 0) - : x(_x), y(_y), z(_z), w(_w) {} - - vec4 operator+(const vec4& rhs) const; - vec4 operator-(const vec4& rhs) const; - - vec4 operator*(fixed rhs) const; - friend vec4 operator*(fixed lhs, const vec4& rhs) { - return rhs * lhs; - } - - fixed operator*(const vec4& rhs) const; - - fixed magnitude_sqr() const; -}; - -} // namespace mtl - -TARGET_END_MODE - diff --git a/src/tests/vec.cpp b/src/tests/vec.cpp new file mode 100644 index 0000000..8ddae30 --- /dev/null +++ b/src/tests/vec.cpp @@ -0,0 +1,261 @@ +#include "mtl/tests/vec.hpp" + +#include "mtl/target.hpp" +#include "mtl/vec.hpp" + +namespace mtl { + +namespace test { + +bool vec_suite::addition_v2() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + vec2 operator()(vec2 a, vec2 b) { + return a + b; + } + } f; + + vec2 c = f(vec2(1, 2), vec2(10, 20)); + + return c.x == 11 && c.y == 22; +} +bool vec_suite::addition_v3() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + vec3 operator()(vec3 a, vec3 b) { + return a + b; + } + } f; + + vec3 c = f(vec3(1, 2, 3), vec3(10, 20, 30)); + + return c.x == 11 && c.y == 22 && c.z == 33; +} +bool vec_suite::addition_v4() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + vec4 operator()(vec4 a, vec4 b) { + return a + b; + } + } f; + + vec4 c = f(vec4(1, 2, 3, 4), vec4(10, 20, 30, 40)); + + return c.x == 11 && c.y == 22 && c.z == 33 && c.w == 44; +} + +bool vec_suite::subtraction_v2() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + vec2 operator()(vec2 a, vec2 b) { + return a - b; + } + } f; + + vec2 c = f(vec2(10, 20), vec2(1, 2)); + + return c.x == 9 && c.y == 18; +} +bool vec_suite::subtraction_v3() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + vec3 operator()(vec3 a, vec3 b) { + return a - b; + } + } f; + + vec3 c = f(vec3(10, 20, 30), vec3(1, 2, 3)); + + return c.x == 9 && c.y == 18 && c.z == 27; +} +bool vec_suite::subtraction_v4() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + vec4 operator()(vec4 a, vec4 b) { + return a - b; + } + } f; + + vec4 c = f(vec4(10, 20, 30, 40), vec4(1, 2, 3, 4)); + + return c.x == 9 && c.y == 18 && c.z == 27 && c.w == 36; +} + +bool vec_suite::negation_v2() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + vec2 operator()(vec2 a) { + return -a; + } + } f; + + vec2 c = f(vec2(10, 20)); + + return c.x == -10 && c.y == -20; +} +bool vec_suite::negation_v3() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + vec3 operator()(vec3 a) { + return -a; + } + } f; + + vec3 c = f(vec3(10, 20, 30)); + + return c.x == -10 && c.y == -20 && c.z == -30; +} +bool vec_suite::negation_v4() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + vec4 operator()(vec4 a) { + return -a; + } + } f; + + vec4 c = f(vec4(10, 20, 30, 40)); + + return c.x == -10 && c.y == -20 && c.z == -30 && c.w == -40; +} + +bool vec_suite::mult_scalar_v2() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + vec2 operator()(vec2 a, fixed x) { + return a * x; + } + } f; + + vec2 c = f(vec2(10, 20), 10); + + return c.x == 100 && c.y == 200; +} +bool vec_suite::mult_scalar_v3() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + vec3 operator()(vec3 a, fixed x) { + return a * x; + } + } f; + + vec3 c = f(vec3(10, 20, 30), 10); + + return c.x == 100 && c.y == 200 && c.z == 300; +} +bool vec_suite::mult_scalar_v4() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + vec4 operator()(vec4 a, fixed x) { + return a * x; + } + } f; + + vec4 c = f(vec4(10, 20, 30, 40), 10); + + return c.x == 100 && c.y == 200 && c.z == 300 && c.w == 400; +} + +bool vec_suite::dot_v2() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + fixed operator()(vec2 a, vec2 b) { + return a * b; + } + } f; + + fixed c = f(vec2(1, 2), vec2(1, 10)); + + return c == 21; +} +bool vec_suite::dot_v3() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + fixed operator()(vec3 a, vec3 b) { + return a * b; + } + } f; + + fixed c = f(vec3(1, 2, 3), vec3(1, 10, 100)); + + return c == 321; +} +bool vec_suite::dot_v4() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + fixed operator()(vec4 a, vec4 b) { + return a * b; + } + } f; + + fixed c = f(vec4(1, 2, 3, 4), vec4(1, 10, 100, 1000)); + + return c == 4321; +} + +bool vec_suite::division_scalar_v2() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + vec2 operator()(vec2 a, fixed x) { + return a / x; + } + } f; + + vec2 c = f(vec2(10, 20), 10); + + return c.x == 1 && c.y == 2; +} +bool vec_suite::division_scalar_v3() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + vec3 operator()(vec3 a, fixed x) { + return a / x; + } + } f; + + vec3 c = f(vec3(10, 20, 30), 10); + + return c.x == 1 && c.y == 2 && c.z == 3; +} +bool vec_suite::division_scalar_v4() { + struct { + NOINLINE GBA_IWRAM ARM_MODE + vec4 operator()(vec4 a, fixed x) { + return a / x; + } + } f; + + vec4 c = f(vec4(10, 20, 30, 40), 10); + + return c.x == 1 && c.y == 2 && c.z == 3 && c.w == 4; +} + +bool vec_suite::magnitude_sqr_v2() { + log::debug << "UNIMPLEMENTED" << endl; + return true; +} +bool vec_suite::magnitude_sqr_v3() { + log::debug << "UNIMPLEMENTED" << endl; + return true; +} +bool vec_suite::magnitude_sqr_v4() { + log::debug << "UNIMPLEMENTED" << endl; + return true; +} + +bool vec_suite::transpose_v2() { + log::debug << "UNIMPLEMENTED" << endl; + return true; +} +bool vec_suite::transpose_v3() { + log::debug << "UNIMPLEMENTED" << endl; + return true; +} +bool vec_suite::transpose_v4() { + log::debug << "UNIMPLEMENTED" << endl; + return true; +} + +} // namespace test + +} // namespace mtl + diff --git a/src/vec4.cpp b/src/vec4.cpp deleted file mode 100644 index 0b95e8e..0000000 --- a/src/vec4.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "mtl/vec4.hpp" - -TARGET_ARM_MODE - -namespace mtl { - -GBA_IWRAM vec4 vec4::operator+(const vec4& rhs) const { - return vec4(x + rhs.x, y + rhs.y, z + rhs.z, w + rhs.w); -} -GBA_IWRAM vec4 vec4::operator-(const vec4& rhs) const { - return vec4(x - rhs.x, y - rhs.y, z - rhs.z, w - rhs.w); -} - -GBA_IWRAM vec4 vec4::operator*(fixed rhs) const { - return vec4(x * rhs, y * rhs, z * rhs, w * rhs); -} - -GBA_IWRAM fixed vec4::operator*(const vec4& rhs) const { - return x * rhs.x + y * rhs.y + z * rhs.z + w * rhs.w; -} - -GBA_IWRAM fixed vec4::magnitude_sqr() const { - return x * x + y * y + z * z + w * w; -} - -} // namespace mtl - -TARGET_END_MODE -