Rewrite math vector implementation and add tests

This new implementation consolidates vec2/3/4 into one base class and
improves performance.
This commit is contained in:
Myles Busig 2024-08-30 15:29:51 -07:00
parent 824792c358
commit 8ac06890b7
5 changed files with 524 additions and 63 deletions

100
include/mtl/tests/vec.hpp Normal file
View File

@ -0,0 +1,100 @@
#pragma once
#include "mtl/test.hpp"
#include "mtl/vec.hpp"
namespace mtl {
namespace test {
class vec_suite : public suite<vec_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

163
include/mtl/vec.hpp Normal file
View File

@ -0,0 +1,163 @@
#pragma once
#include "mtl/target.hpp"
#include <cstddef>
#include "mtl/fixed.hpp"
namespace mtl {
template <size_t M, size_t N>
class mat;
template <size_t N>
class vec {
public:
fixed e[N];
constexpr vec() noexcept {}
constexpr vec(const vec<N>& 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<N> operator+(const vec<N>& rhs) const noexcept {
vec<N> res;
for (size_t i = 0; i < N; ++i) {
res[i] = e[i] + rhs[i];
}
return res;
}
vec<N> operator-(const vec<N>& rhs) const noexcept {
vec<N> res;
for (size_t i = 0; i < N; ++i) {
res[i] = e[i] - rhs[i];
}
return res;
}
vec<N> operator-() const noexcept {
vec<N> res;
for (size_t i = 0; i < N; ++i) {
res[i] = -e[i];
}
return res;
}
vec<N> operator*(fixed rhs) const noexcept {
vec<N> res;
for (size_t i = 0; i < N; ++i) {
res[i] = e[i] * rhs;
}
return res;
}
friend vec<N> operator*(fixed lhs, vec<N> rhs) noexcept {
return rhs * lhs;
}
fixed operator*(const vec<N>& rhs) const noexcept {
fixed res;
for (size_t i = 0; i < N; ++i) {
res += e[i] * rhs[i];
}
return res;
}
vec<N> operator/(fixed rhs) const noexcept {
vec<N> 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

View File

@ -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

261
src/tests/vec.cpp Normal file
View File

@ -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

View File

@ -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