Compare commits
No commits in common. "fiber" and "trunk" have entirely different histories.
@ -3,17 +3,21 @@ cmake_minimum_required(VERSION 3.5)
|
|||||||
project(mtl LANGUAGES CXX C ASM)
|
project(mtl LANGUAGES CXX C ASM)
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
option(BUILD_TESTS "Build tests" ON)
|
||||||
|
|
||||||
message("Compiling MTL for ${CMAKE_SYSTEM_PROCESSOR}")
|
message("Compiling MTL for ${CMAKE_SYSTEM_PROCESSOR}")
|
||||||
|
|
||||||
# CONFIGURE_DEPENDS option was added in CMake v3.12. This option allows the
|
# CONFIGURE_DEPENDS option was added in CMake v3.12. This option allows the
|
||||||
# build system to automatically re-run CMake if the glob changes, solving the
|
# build system to automatically re-run CMake if the glob changes, solving the
|
||||||
# major issue with globbing. May have a performance impact, but it should be
|
# major issue with globbing. May have a performance impact, but it should be
|
||||||
# negligible compared to the time spent building.
|
# negligible compared to the time spent building.
|
||||||
file(GLOB mtl_sources_common LIST_DIRECTORIES false CONFIGURE_DEPENDS src/*.cpp src/*.c src/*.s)
|
file(GLOB mtl_sources_common LIST_DIRECTORIES false CONFIGURE_DEPENDS src/common/*.cpp src/common/*.c src/common/*.s)
|
||||||
file(GLOB mtl_sources_armv4t LIST_DIRECTORIES false CONFIGURE_DEPENDS src/armv4t/*.cpp src/armv4t/*.c src/armv4t/*.s)
|
file(GLOB mtl_sources_armv4t LIST_DIRECTORIES false CONFIGURE_DEPENDS src/armv4t/*.cpp src/armv4t/*.c src/armv4t/*.s)
|
||||||
file(GLOB mtl_sources_gba LIST_DIRECTORIES false CONFIGURE_DEPENDS src/gba/*.cpp src/gba/*.c src/gba/*.s)
|
file(GLOB mtl_sources_gba LIST_DIRECTORIES false CONFIGURE_DEPENDS src/gba/*.cpp src/gba/*.c src/gba/*.s)
|
||||||
|
file(GLOB mtl_sources_test LIST_DIRECTORIES false CONFIGURE_DEPENDS src/test/*.cpp)
|
||||||
|
|
||||||
set(mtl_include_common "include")
|
set(mtl_include_common "include")
|
||||||
|
set(mtl_include_test "include/test")
|
||||||
set(mtl_include_gba "include/gba")
|
set(mtl_include_gba "include/gba")
|
||||||
set(mtl_include_armv4t "include/armv4t")
|
set(mtl_include_armv4t "include/armv4t")
|
||||||
|
|
||||||
@ -39,6 +43,14 @@ if (CMAKE_SYSTEM_PROCESSOR STREQUAL "armv4t")
|
|||||||
target_link_libraries(${PROJECT_NAME} PUBLIC ${PROJECT_NAME}_armv4t)
|
target_link_libraries(${PROJECT_NAME} PUBLIC ${PROJECT_NAME}_armv4t)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (BUILD_TESTS STREQUAL "ON")
|
||||||
|
add_library(${PROJECT_NAME}_test OBJECT)
|
||||||
|
target_sources(${PROJECT_NAME}_test PRIVATE "${mtl_sources_test}")
|
||||||
|
target_include_directories(${PROJECT_NAME}_test PUBLIC "${mtl_include_common}")
|
||||||
|
target_include_directories(${PROJECT_NAME}_test PUBLIC "${mtl_include_test}")
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC ${PROJECT_NAME}_test)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_sources(${PROJECT_NAME}_common PRIVATE "${mtl_sources_common}")
|
target_sources(${PROJECT_NAME}_common PRIVATE "${mtl_sources_common}")
|
||||||
target_include_directories(${PROJECT_NAME}_common PUBLIC "${mtl_include_common}")
|
target_include_directories(${PROJECT_NAME}_common PUBLIC "${mtl_include_common}")
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC ${PROJECT_NAME}_common)
|
target_link_libraries(${PROJECT_NAME} PUBLIC ${PROJECT_NAME}_common)
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
# mtl
|
# mtl
|
||||||
|
|
||||||
C++ STL replacement, intended for embedded systems. Aims to be more performant than ETL.
|
An embedded data structure and linear algebra library built for the GBA. Aims to be
|
||||||
|
more performant than the ETL by taking advantage of GBA-specific features.
|
||||||
|
|||||||
@ -1,164 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
#ifndef MTL_FIBER_MAX_ACTIVE
|
|
||||||
#define MTL_FIBER_MAX_ACTIVE 4
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MTL_FIBER_MAX_QUEUE
|
|
||||||
#define MTL_FIBER_MAX_QUEUE 256
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MTL_FIBER_MAX_QUEUE_LOW
|
|
||||||
#define MTL_FIBER_MAX_QUEUE_LOW MTL_FIBER_MAX_QUEUE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MTL_FIBER_MAX_QUEUE_MED
|
|
||||||
#define MTL_FIBER_MAX_QUEUE_MED MTL_FIBER_MAX_QUEUE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MTL_FIBER_MAX_QUEUE_HI
|
|
||||||
#define MTL_FIBER_MAX_QUEUE_HI MTL_FIBER_MAX_QUEUE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MTL_FIBER_STACK_SIZE
|
|
||||||
#define MTL_FIBER_STACK_SIZE 4096
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MTL_FIBER_MAX_DEPENDENTS
|
|
||||||
#define MTL_FIBER_MAX_DEPENDENTS 8
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern "C" void mtl_yield();
|
|
||||||
|
|
||||||
namespace mtl {
|
|
||||||
|
|
||||||
constexpr auto yield = mtl_yield;
|
|
||||||
|
|
||||||
// TEMPORARY
|
|
||||||
template <typename T, size_t N>
|
|
||||||
class vector {
|
|
||||||
public:
|
|
||||||
void push_back(T x) {}
|
|
||||||
|
|
||||||
T* begin() {return nullptr;}
|
|
||||||
T* end() { return nullptr;}
|
|
||||||
void clear();
|
|
||||||
constexpr size_t capacity() { return N; }
|
|
||||||
size_t size() { return 0; }
|
|
||||||
};
|
|
||||||
|
|
||||||
using fiber_stack = uint8_t[MTL_FIBER_STACK_SIZE];
|
|
||||||
|
|
||||||
class ifiber;
|
|
||||||
|
|
||||||
class ifiber_dependency {
|
|
||||||
protected:
|
|
||||||
mtl::vector<ifiber*, MTL_FIBER_MAX_DEPENDENTS> m_dependents;
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
public:
|
|
||||||
void add_dependent(ifiber* dependent);
|
|
||||||
};
|
|
||||||
|
|
||||||
class fiber_trigger;
|
|
||||||
|
|
||||||
extern ifiber* this_fiber;
|
|
||||||
|
|
||||||
class ifiber : public ifiber_dependency {
|
|
||||||
public:
|
|
||||||
enum class priority {
|
|
||||||
low,
|
|
||||||
med,
|
|
||||||
hi,
|
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
friend class ifiber_dependency;
|
|
||||||
|
|
||||||
static mtl::vector<ifiber*, MTL_FIBER_MAX_QUEUE_LOW> s_queue_low;
|
|
||||||
static mtl::vector<ifiber*, MTL_FIBER_MAX_QUEUE_MED> s_queue_med;
|
|
||||||
static mtl::vector<ifiber*, MTL_FIBER_MAX_QUEUE_HI> s_queue_hi;
|
|
||||||
|
|
||||||
bool m_finished;
|
|
||||||
priority m_priority;
|
|
||||||
uint32_t m_dependencies;
|
|
||||||
|
|
||||||
ifiber(priority priority) : m_priority(priority), m_finished(false), m_dependencies(0) {
|
|
||||||
switch (priority) {
|
|
||||||
case priority::low:
|
|
||||||
// If the queue is already full, a currently running
|
|
||||||
// fiber must finish first to free room in the queue.
|
|
||||||
// If all currently running fibers are waiting for a
|
|
||||||
// dependency, we will throw an exception.
|
|
||||||
if (s_queue_low.size() == s_queue_low.capacity()) {
|
|
||||||
mtl::yield();
|
|
||||||
}
|
|
||||||
s_queue_low.push_back(this);
|
|
||||||
break;
|
|
||||||
case priority::med:
|
|
||||||
if (s_queue_med.size() == s_queue_med.capacity()) {
|
|
||||||
mtl::yield();
|
|
||||||
}
|
|
||||||
s_queue_med.push_back(this);
|
|
||||||
break;
|
|
||||||
case priority::hi:
|
|
||||||
if (s_queue_hi.size() == s_queue_hi.capacity()) {
|
|
||||||
mtl::yield();
|
|
||||||
}
|
|
||||||
s_queue_hi.push_back(this);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
};
|
|
||||||
|
|
||||||
class fiber_trigger : public ifiber_dependency {
|
|
||||||
public:
|
|
||||||
void trigger() {
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template <typename RetType, typename... ArgTypes>
|
|
||||||
class fiber : public ifiber {
|
|
||||||
public:
|
|
||||||
using FuncType = RetType(*)(ArgTypes...);
|
|
||||||
|
|
||||||
private:
|
|
||||||
FuncType m_func;
|
|
||||||
std::tuple<ArgTypes...> m_args;
|
|
||||||
|
|
||||||
public:
|
|
||||||
fiber(FuncType func, ArgTypes... args)
|
|
||||||
: ifiber(priority::med), m_func(func), m_args(args...) {}
|
|
||||||
|
|
||||||
fiber(FuncType func, ArgTypes... args, priority priority)
|
|
||||||
: ifiber(priority), m_func(func), m_args(args...) {}
|
|
||||||
|
|
||||||
// TODO: Add priority+dependency constructors
|
|
||||||
|
|
||||||
fiber(FuncType func, ArgTypes... args, const ifiber_dependency& dep)
|
|
||||||
: ifiber(priority::med), m_func(func), m_args(args...) {
|
|
||||||
dep.add_dependent(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename It>
|
|
||||||
fiber(FuncType func, ArgTypes... args, It begin, It end)
|
|
||||||
: ifiber(priority::med), m_func(func), m_args(args...) {
|
|
||||||
for (; begin != end; ++begin) {
|
|
||||||
begin->add_dependent(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void join() {
|
|
||||||
this_fiber->add_dependent(this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mtl
|
|
||||||
@ -1,58 +1,106 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
/**
|
#include "mtl/target.hpp"
|
||||||
* \brief Fixed point multiply assembly implementation
|
|
||||||
*
|
|
||||||
* DO NOT USE DIRECTLY! Use fixed::operator* instead
|
|
||||||
*/
|
|
||||||
extern "C" int32_t mtl_fixed_mul(int32_t x, int32_t y);
|
|
||||||
|
|
||||||
namespace mtl {
|
namespace mtl {
|
||||||
/**
|
/**
|
||||||
* \brief 32-bit Fixed point number
|
* \brief 32-bit Fixed point number
|
||||||
*
|
*
|
||||||
* Uses a base of 64. ie. the lower 6 bits are after the decimal place,
|
* Uses a base of 256. ie. the lower 8 bits are after the decimal place,
|
||||||
* the other 26 bits are before the decimal place.
|
* the next 23 bits are before the decimal place, and one negation bit.
|
||||||
*
|
*
|
||||||
* Valid values are in the range ~[-33'554'431.01, 33'554'432.98]
|
* Valid values are in the range ~[-8'388'306.000, 8'388'607.996]
|
||||||
*
|
*
|
||||||
* Has a maximum error of +/- 1/128 (~0.0078), integers are always
|
* Has a maximum error of +/- 1/512 (~0.0019), integers are always
|
||||||
* exactly.
|
* exactly represented. Keep in mind, this error accumulates each time an operation
|
||||||
|
* is performed. For example, when computing a vec4 projection matrix, the may
|
||||||
|
* diverge by closer to 0.0156.
|
||||||
|
*
|
||||||
|
* \par ARM
|
||||||
|
*
|
||||||
|
* All functions are compiled in ARM-mode because some operators (notably
|
||||||
|
* multiplication and division) are much faster in ARM-mode. For optimal
|
||||||
|
* performance, fixed point numbers should only be used in ARM-mode
|
||||||
|
* code to enable as much inlining as possible.
|
||||||
*/
|
*/
|
||||||
class fixed {
|
class fixed {
|
||||||
|
private:
|
||||||
int32_t x;
|
int32_t x;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Raw constructor
|
* \brief Raw constructor
|
||||||
*
|
*
|
||||||
* Creates a new fixed point number with the raw data of x.
|
* Creates a new fixed point number with the raw data of x.
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
*
|
||||||
|
* DO NOT USE DIRECTLY. Use `from_raw` instead.
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
*
|
||||||
* DO NOT use to set the fixed number to an integer value, use
|
* DO NOT use to set the fixed number to an integer value, use
|
||||||
* the public constructor instead.
|
* the public constructor instead.
|
||||||
*/
|
*/
|
||||||
constexpr fixed(int32_t _x, bool) : x(_x) {}
|
ARM_MODE
|
||||||
|
constexpr fixed(int32_t _x, bool) noexcept : x(_x) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr fixed() : x(0) {}
|
ARM_MODE
|
||||||
|
constexpr fixed() noexcept : x(0) {}
|
||||||
/**
|
/**
|
||||||
* \brief 32-bit integer constructor
|
* \brief Integer constructor
|
||||||
*
|
*
|
||||||
* Creates a new fixed point number with the value of the integer.
|
* Creates a new fixed point number with the value of the integer.
|
||||||
* Must be within the range represented by fixed point numbers, see
|
* Must be within the range represented by fixed point numbers, see
|
||||||
* the class description for more detail.
|
* the class description for more detail.
|
||||||
*/
|
*/
|
||||||
constexpr fixed(int32_t _i) : x(_i * 64) {}
|
template <typename T, std::enable_if_t<std::is_integral_v<T>, bool> = true>
|
||||||
|
ARM_MODE
|
||||||
|
constexpr fixed(T _i) noexcept : x(_i * 256) {}
|
||||||
/**
|
/**
|
||||||
* \brief Floating point constructor
|
* \brief Floating point constructor
|
||||||
*
|
*
|
||||||
* Creates a new fixed point number with the closest number to
|
* Creates a new fixed point number with the closest number to
|
||||||
* the floating point number. Must be within the range represented by
|
* the floating point number. Must be within the range represented by
|
||||||
* fixed point numbers, see the class description for more detail.
|
* fixed point numbers, see the class description for more detail.
|
||||||
|
*
|
||||||
|
* Must be implemented as a template with enable_if, otherwise passing
|
||||||
|
* an int (not int32_t) is ambiguous between the promotion to int32_t and
|
||||||
|
* float.
|
||||||
*/
|
*/
|
||||||
constexpr fixed(float _f)
|
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
|
||||||
|
ARM_MODE
|
||||||
|
constexpr fixed(T _f) noexcept
|
||||||
// 0.5 offset accounts for truncating to integer, round instead
|
// 0.5 offset accounts for truncating to integer, round instead
|
||||||
: x((_f * 64) + 0.5f) {}
|
: x((_f * 256) + 0.5f) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Raw value factory
|
||||||
|
*
|
||||||
|
* Creates a new fixed point number with the raw data of x.
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
*
|
||||||
|
* Should not be used unless absolutely needed.
|
||||||
|
*/
|
||||||
|
ARM_MODE
|
||||||
|
static constexpr fixed from_raw(int32_t x) noexcept {
|
||||||
|
return fixed(x, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Raw value accessor
|
||||||
|
*
|
||||||
|
* Gets the raw value of the fixed point number. i.e. The fixed point
|
||||||
|
* number multiplied by 256.
|
||||||
|
*/
|
||||||
|
ARM_MODE
|
||||||
|
constexpr int32_t raw() const noexcept {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Fixed point addition
|
* \brief Fixed point addition
|
||||||
@ -60,24 +108,96 @@ public:
|
|||||||
* Addition with fixed point numbers is the same as with a 32-bit
|
* Addition with fixed point numbers is the same as with a 32-bit
|
||||||
* integer, so should be extremely quick.
|
* integer, so should be extremely quick.
|
||||||
*/
|
*/
|
||||||
fixed operator+(fixed rhs) const {
|
ARM_MODE
|
||||||
return fixed(x + rhs.x, true);
|
constexpr fixed operator+(fixed rhs) const noexcept {
|
||||||
|
return from_raw(x + rhs.x);
|
||||||
|
}
|
||||||
|
ARM_MODE
|
||||||
|
constexpr fixed& operator+=(fixed rhs) noexcept {
|
||||||
|
x += rhs.x;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* \brief Fixed point subtraction
|
||||||
|
*/
|
||||||
|
ARM_MODE
|
||||||
|
constexpr fixed operator-(fixed rhs) const noexcept {
|
||||||
|
return from_raw(x - rhs.x);
|
||||||
|
}
|
||||||
|
ARM_MODE
|
||||||
|
constexpr fixed& operator-=(fixed rhs) noexcept {
|
||||||
|
x -= rhs.x;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ARM_MODE
|
||||||
|
constexpr fixed operator-() const noexcept {
|
||||||
|
return from_raw(-x);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Fixed point multiplication
|
* \brief Fixed point multiplication
|
||||||
*
|
|
||||||
* Uses an assembly implementation to multiply the two numbers.
|
|
||||||
* Not as quick as an integer multiplication. Use sparringly.
|
|
||||||
*
|
|
||||||
* Tested on the MGBA Gameboy Advance emulator, takes around 70
|
|
||||||
* cycles when the assembly routine is placed in IWRAM.
|
|
||||||
* The Gameboy Advance uses an armv7tdmi, and IWRAM is the fastest
|
|
||||||
* available RAM.
|
|
||||||
*/
|
*/
|
||||||
fixed operator*(fixed rhs) const {
|
ARM_MODE
|
||||||
return fixed(mtl_fixed_mul(x, rhs.x), true);
|
constexpr fixed operator*(fixed rhs) const noexcept {
|
||||||
|
return from_raw(((int64_t)x * rhs.x) >> 8);
|
||||||
|
}
|
||||||
|
ARM_MODE
|
||||||
|
constexpr fixed& operator*=(fixed rhs) noexcept {
|
||||||
|
*this = *this * rhs;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Fixed point division
|
||||||
|
*
|
||||||
|
* Faster for numerators in domain [-0x7FFFF, 0x7FFFF].
|
||||||
|
*
|
||||||
|
* On attempted division by zero, the result is set to the largest
|
||||||
|
* absolute value possible with the same sign as the numerator. This means
|
||||||
|
* that if a denominator slowly approaches zero, once it reaches zero
|
||||||
|
* the quotient's sign will flip. The largest value is used because fixed
|
||||||
|
* point numbers don't have a representation of infinity.
|
||||||
|
*
|
||||||
|
* \par GBA
|
||||||
|
*
|
||||||
|
* Placed in IWRAM
|
||||||
|
*/
|
||||||
|
ARM_MODE GBA_IWRAM
|
||||||
|
fixed operator/(fixed rhs) const noexcept;
|
||||||
|
ARM_MODE
|
||||||
|
fixed& operator/=(fixed rhs) noexcept {
|
||||||
|
*this = *this / rhs;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Comparison operators
|
||||||
|
*/
|
||||||
|
constexpr bool operator==(fixed rhs) const noexcept {
|
||||||
|
return x == rhs.x;
|
||||||
|
}
|
||||||
|
constexpr bool operator!=(fixed rhs) const noexcept {
|
||||||
|
return x != rhs.x;
|
||||||
|
}
|
||||||
|
constexpr bool operator>(fixed rhs) const noexcept {
|
||||||
|
return x > rhs.x;
|
||||||
|
}
|
||||||
|
constexpr bool operator>=(fixed rhs) const noexcept {
|
||||||
|
return x >= rhs.x;
|
||||||
|
}
|
||||||
|
constexpr bool operator<(fixed rhs) const noexcept {
|
||||||
|
return x < rhs.x;
|
||||||
|
}
|
||||||
|
constexpr bool operator<=(fixed rhs) const noexcept {
|
||||||
|
return x <= rhs.x;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename STREAM_TYPE>
|
||||||
|
STREAM_TYPE operator<<(STREAM_TYPE& lhs, fixed rhs) {
|
||||||
|
lhs << rhs.raw();
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mtl
|
} // namespace mtl
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,8 @@ constexpr char endl_char = 0;
|
|||||||
constexpr char endl_char = '\n';
|
constexpr char endl_char = '\n';
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
using stream_type = basic_string_stream<false, endl_char>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Log stream
|
* \brief Log stream
|
||||||
*
|
*
|
||||||
@ -42,7 +44,7 @@ constexpr char endl_char = '\n';
|
|||||||
* On MGBA this also starts the line with "GBA Debug: ",so the line is not
|
* On MGBA this also starts the line with "GBA Debug: ",so the line is not
|
||||||
* completely blank either.
|
* completely blank either.
|
||||||
*/
|
*/
|
||||||
class stream : public basic_string_stream<false, endl_char> {
|
class stream : public stream_type {
|
||||||
private:
|
private:
|
||||||
level m_log_level;
|
level m_log_level;
|
||||||
|
|
||||||
|
|||||||
93
include/mtl/mat.hpp
Normal file
93
include/mtl/mat.hpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "mtl/target.hpp"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "mtl/fixed.hpp"
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
class vec;
|
||||||
|
|
||||||
|
template <size_t M, size_t N>
|
||||||
|
class mat {
|
||||||
|
public:
|
||||||
|
fixed e[M][N];
|
||||||
|
|
||||||
|
constexpr mat() noexcept {}
|
||||||
|
|
||||||
|
mat(const vec<M>& v) noexcept {
|
||||||
|
for (size_t i = 0; i < M; ++i) {
|
||||||
|
e[i][0] = v[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
constexpr mat(const fixed(&_e)[M][N]) noexcept {
|
||||||
|
for (size_t i = 0; i < M; ++i) {
|
||||||
|
for (size_t j = 0; j < N; ++j) {
|
||||||
|
e[i][j] = _e[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec<N> row(size_t i) const noexcept {
|
||||||
|
return vec<N>(e[i]);
|
||||||
|
}
|
||||||
|
vec<M> col(size_t i) const noexcept {
|
||||||
|
vec<M> r;
|
||||||
|
|
||||||
|
for (size_t j = 0; j < M; ++j) {
|
||||||
|
r.e[j] = e[j][i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
ARM_MODE GBA_IWRAM
|
||||||
|
mat<M, N> operator+(const mat<M, N>& rhs) const;
|
||||||
|
ARM_MODE GBA_IWRAM
|
||||||
|
mat<M, N> operator-(const mat<M, N>& rhs) const;
|
||||||
|
|
||||||
|
ARM_MODE GBA_IWRAM
|
||||||
|
mat<M, N> operator*(fixed rhs) const;
|
||||||
|
ARM_MODE GBA_IWRAM
|
||||||
|
mat<M, N> operator/(fixed rhs) const;
|
||||||
|
|
||||||
|
ARM_MODE GBA_IWRAM
|
||||||
|
vec<M> operator*(const vec<N>& rhs) const noexcept;
|
||||||
|
|
||||||
|
ARM_MODE GBA_IWRAM
|
||||||
|
bool operator==(const mat<M, N>& rhs) const;
|
||||||
|
bool operator!=(const mat<M, N>& rhs) const {
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t RHS_N>
|
||||||
|
ARM_MODE GBA_IWRAM
|
||||||
|
mat<M, RHS_N> operator*(const mat<N, RHS_N>& rhs) const noexcept;
|
||||||
|
|
||||||
|
template <size_t S>
|
||||||
|
friend mat<S, S> operator*(const vec<S>& lhs, const mat<1, S>& rhs);
|
||||||
|
|
||||||
|
template <size_t D = (M == N ? M : 0), std::enable_if_t<D != 0, bool> = true>
|
||||||
|
static mat<D, D> identity() {
|
||||||
|
mat<D, D> r;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < D; ++i) {
|
||||||
|
r.e[i][i] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t D = (M == N ? M : 0), std::enable_if_t<D == 4, bool> = true>
|
||||||
|
static mat<M, N> translation(const vec<D - 1>& v); // Not defined in header because `v` will rarely be an r-value
|
||||||
|
};
|
||||||
|
|
||||||
|
template <size_t S>
|
||||||
|
ARM_MODE GBA_IWRAM
|
||||||
|
mat<S, S> operator*(const vec<S>& lhs, const mat<1, S>& rhs);
|
||||||
|
|
||||||
|
} // namespace mtl
|
||||||
|
|
||||||
136
include/mtl/mat_impl.hpp
Normal file
136
include/mtl/mat_impl.hpp
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* This file should only be included by files that explicitly instantiate
|
||||||
|
* matrix classes.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#pragma GCC push_options
|
||||||
|
#pragma GCC optimize("unroll-loops")
|
||||||
|
|
||||||
|
#include "mtl/mat.hpp"
|
||||||
|
|
||||||
|
#include "mtl/vec.hpp"
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
|
||||||
|
template <size_t M, size_t N>
|
||||||
|
mat<M, N> mat<M, N>::operator+(const mat<M, N>& rhs) const {
|
||||||
|
mat<M, N> r;
|
||||||
|
|
||||||
|
for (size_t im = 0; im < M; ++im) {
|
||||||
|
for (size_t in = 0; in < N; ++in) {
|
||||||
|
r.e[im][in] = e[im][in] + rhs.e[im][in];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
template <size_t M, size_t N>
|
||||||
|
mat<M, N> mat<M, N>::operator-(const mat<M, N>& rhs) const {
|
||||||
|
mat<M, N> r;
|
||||||
|
|
||||||
|
for (size_t im = 0; im < M; ++im) {
|
||||||
|
for (size_t in = 0; in < N; ++in) {
|
||||||
|
r.e[im][in] = e[im][in] - rhs.e[im][in];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t M, size_t N>
|
||||||
|
mat<M, N> mat<M, N>::operator*(fixed rhs) const {
|
||||||
|
mat<M, N> r;
|
||||||
|
|
||||||
|
for (size_t im = 0; im < M; ++im) {
|
||||||
|
for (size_t in = 0; in < N; ++in) {
|
||||||
|
r.e[im][in] = e[im][in] * rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
template <size_t M, size_t N>
|
||||||
|
mat<M, N> mat<M, N>::operator/(fixed rhs) const {
|
||||||
|
mat<M, N> r;
|
||||||
|
|
||||||
|
for (size_t im = 0; im < M; ++im) {
|
||||||
|
for (size_t in = 0; in < N; ++in) {
|
||||||
|
r.e[im][in] = e[im][in] / rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t M, size_t N>
|
||||||
|
vec<M> mat<M, N>::operator*(const vec<N>& rhs) const noexcept {
|
||||||
|
vec<M> r;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < M; ++i) {
|
||||||
|
r.e[i] = row(i) * rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t M, size_t N>
|
||||||
|
template <size_t RHS_N>
|
||||||
|
mat<M, RHS_N> mat<M, N>::operator*(const mat<N, RHS_N>& rhs) const noexcept {
|
||||||
|
mat<M, RHS_N> r;
|
||||||
|
|
||||||
|
for (size_t im = 0; im < M; ++im) {
|
||||||
|
for (size_t in = 0; in < RHS_N; ++in) {
|
||||||
|
r.e[im][in] = row(im) * rhs.col(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t S>
|
||||||
|
mat<S, S> operator*(const vec<S>& lhs, const mat<1, S>& rhs) {
|
||||||
|
mat<S, S> r;
|
||||||
|
|
||||||
|
for (size_t im = 0; im < S; ++im) {
|
||||||
|
for (size_t in = 0; in < S; ++in) {
|
||||||
|
r.e[im][in] = lhs[im] * rhs.e[0][in];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t M, size_t N>
|
||||||
|
bool mat<M, N>::operator==(const mat<M, N>& rhs) const {
|
||||||
|
for (size_t im = 0; im < M; ++im) {
|
||||||
|
for (size_t in = 0; in < N; ++in) {
|
||||||
|
if (e[im][in] != rhs.e[im][in]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
template <size_t D, std::enable_if_t<D == 4, bool>>
|
||||||
|
mat<4, 4> mat<4, 4>::translation(const vec<D - 1>& v) {
|
||||||
|
mat<4, 4> r;
|
||||||
|
|
||||||
|
r.e[0][0] = 1;
|
||||||
|
r.e[1][1] = 1;
|
||||||
|
r.e[2][2] = 1;
|
||||||
|
r.e[3][3] = 1;
|
||||||
|
|
||||||
|
r.e[0][3] = v.e[0];
|
||||||
|
r.e[1][3] = v.e[1];
|
||||||
|
r.e[2][3] = v.e[2];
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mtl
|
||||||
|
|
||||||
|
#pragma GCC pop_options
|
||||||
|
|
||||||
92
include/mtl/queue.hpp
Normal file
92
include/mtl/queue.hpp
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <mtl/exception.hpp>
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Statically allocated queue
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class iqueue {
|
||||||
|
private:
|
||||||
|
T* m_buf;
|
||||||
|
size_t m_size;
|
||||||
|
const size_t m_capacity;
|
||||||
|
|
||||||
|
size_t m_begin;
|
||||||
|
size_t m_end;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
using size_type = size_t;
|
||||||
|
using reference = T&;
|
||||||
|
using const_reference = const T&;
|
||||||
|
|
||||||
|
iqueue(T* buf, size_t capacity) noexcept : m_buf(buf), m_capacity(capacity), m_begin(0), m_end(0) {}
|
||||||
|
|
||||||
|
reference front() noexcept {
|
||||||
|
return m_buf[m_begin];
|
||||||
|
}
|
||||||
|
const_reference front() const noexcept {
|
||||||
|
return m_buf[m_begin];
|
||||||
|
}
|
||||||
|
const_reference cfront() const noexcept {
|
||||||
|
return m_buf[m_begin];
|
||||||
|
}
|
||||||
|
|
||||||
|
reference back() noexcept {
|
||||||
|
return m_buf[m_end];
|
||||||
|
}
|
||||||
|
const_reference back() const noexcept {
|
||||||
|
return m_buf[m_end];
|
||||||
|
}
|
||||||
|
const_reference cback() const noexcept {
|
||||||
|
return m_buf[m_end];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const noexcept {
|
||||||
|
return m_size == 0;
|
||||||
|
}
|
||||||
|
size_t size() const noexcept {
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(T value) {
|
||||||
|
if (m_size == m_capacity) {
|
||||||
|
throw mtl::length_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_buf[m_end] = std::move(value);
|
||||||
|
|
||||||
|
++m_size;
|
||||||
|
|
||||||
|
++m_end;
|
||||||
|
m_end %= m_capacity;
|
||||||
|
}
|
||||||
|
void pop() noexcept {
|
||||||
|
if (m_size == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
--m_size;
|
||||||
|
|
||||||
|
++m_begin;
|
||||||
|
m_begin %= m_capacity;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, size_t C>
|
||||||
|
class queue : public iqueue<T> {
|
||||||
|
private:
|
||||||
|
T m_buf[C];
|
||||||
|
|
||||||
|
public:
|
||||||
|
queue() noexcept : iqueue<T>(m_buf, C) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mtl
|
||||||
|
|
||||||
@ -19,8 +19,8 @@ namespace mtl {
|
|||||||
class istring {
|
class istring {
|
||||||
protected:
|
protected:
|
||||||
char* m_str;
|
char* m_str;
|
||||||
size_t m_size;
|
|
||||||
size_t m_capacity;
|
size_t m_capacity;
|
||||||
|
size_t m_size;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static constexpr const size_t npos = -1;
|
static constexpr const size_t npos = -1;
|
||||||
|
|||||||
@ -85,6 +85,13 @@ public:
|
|||||||
int32_t compare(size_t pos, size_t count, const string_view& str) const;
|
int32_t compare(size_t pos, size_t count, const string_view& str) const;
|
||||||
int32_t compare(size_t pos1, size_t count1, const string_view& str, size_t pos2, size_t count2 = npos) const;
|
int32_t compare(size_t pos1, size_t count1, const string_view& str, size_t pos2, size_t count2 = npos) const;
|
||||||
|
|
||||||
|
bool operator==(const string_view& rhs) const;
|
||||||
|
bool operator!=(const string_view& rhs) const;
|
||||||
|
bool operator<(const string_view& rhs) const;
|
||||||
|
bool operator<=(const string_view& rhs) const;
|
||||||
|
bool operator>(const string_view& rhs) const;
|
||||||
|
bool operator>=(const string_view& rhs) const;
|
||||||
|
|
||||||
friend bool operator==(const istring& lhs, const istring& rhs);
|
friend bool operator==(const istring& lhs, const istring& rhs);
|
||||||
friend bool operator!=(const istring& lhs, const istring& rhs);
|
friend bool operator!=(const istring& lhs, const istring& rhs);
|
||||||
friend bool operator<(const istring& lhs, const istring& rhs);
|
friend bool operator<(const istring& lhs, const istring& rhs);
|
||||||
|
|||||||
48
include/mtl/target.hpp
Normal file
48
include/mtl/target.hpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef __GNUC__
|
||||||
|
#error Failed to create target macros. Compiler is not GCC.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NOINLINE [[gnu::noinline]]
|
||||||
|
#define ALWAYS_INLINE [[gnu::always_inline]]
|
||||||
|
|
||||||
|
#ifdef __arm__
|
||||||
|
|
||||||
|
#define TARGET_ARM_MODE _Pragma("GCC push_options") _Pragma("GCC target(\"arm\")")
|
||||||
|
#define TARGET_THUMB_MODE _Pragma("GCC push_options") _Pragma("GCC target(\"thumb\")")
|
||||||
|
#define TARGET_END_MODE _Pragma("GCC pop_options")
|
||||||
|
#define ARM_MODE [[gnu::target("arm")]]
|
||||||
|
#define THUMB_MODE [[gnu::target("thumb")]]
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define TARGET_ARM_MODE
|
||||||
|
#define TARGET_THUMB_MODE
|
||||||
|
#define TARGET_END_MODE
|
||||||
|
#define ARM_MODE
|
||||||
|
#define THUMB_MODE
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GBA__
|
||||||
|
|
||||||
|
// If a section attribute is used, the symbol will be placed in the section
|
||||||
|
// exactly as specified. This means that when -ffunction-sections is used, functions
|
||||||
|
// will no longer be placed in separate sections. This macro is used to mimic
|
||||||
|
// the effect of -ffunction-sections while still placing the function in IWRAM.
|
||||||
|
#define GBA_IWRAM_FUNC(f) [[gnu::section(".iwram." #f), gnu::long_call]]
|
||||||
|
#define GBA_IWRAM [[gnu::section(".iwram"), gnu::long_call]]
|
||||||
|
#define GBA_EWRAM [[gnu::section(".ewram"), gnu::long_call]]
|
||||||
|
#define GBA_IWRAM_DATA [[gnu::section(".iwram")]]
|
||||||
|
#define GBA_EWRAM_DATA [[gnu::section(".ewram")]]
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define GBA_IWRAM
|
||||||
|
#define GBA_EWRAM
|
||||||
|
#define GBA_IWRAM_DATA
|
||||||
|
#define GBA_EWRAM_DATA
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
233
include/mtl/vec.hpp
Normal file
233
include/mtl/vec.hpp
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
#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.
|
||||||
|
#pragma GCC unroll 4 // Force unroll loops. Can't use pragmas or attributes
|
||||||
|
// because they don't work for inlined functions. Requires
|
||||||
|
// GCC 8.1
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
e[i] = other.e[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
constexpr vec(const fixed (&_e)[N]) noexcept {
|
||||||
|
#pragma GCC unroll 4
|
||||||
|
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;
|
||||||
|
|
||||||
|
#pragma GCC unroll 4
|
||||||
|
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;
|
||||||
|
|
||||||
|
#pragma GCC unroll 4
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
res[i] = e[i] - rhs[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec<N> operator-() const noexcept {
|
||||||
|
vec<N> res;
|
||||||
|
|
||||||
|
#pragma GCC unroll 4
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
res[i] = -e[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec<N> operator*(fixed rhs) const noexcept {
|
||||||
|
vec<N> res;
|
||||||
|
|
||||||
|
#pragma GCC unroll 4
|
||||||
|
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;
|
||||||
|
|
||||||
|
#pragma GCC unroll 4
|
||||||
|
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;
|
||||||
|
|
||||||
|
#pragma GCC unroll 4
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
r[i] = e[i] / rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed magnitude_sqr() const noexcept {
|
||||||
|
fixed r;
|
||||||
|
|
||||||
|
#pragma GCC unroll 4
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
r += e[i] * e[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat<1, N> transpose() const noexcept {
|
||||||
|
mat<1, N> r;
|
||||||
|
|
||||||
|
#pragma GCC unroll 4
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
r.e[0][i] = e[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const vec<N>& rhs) noexcept {
|
||||||
|
#pragma GCC unroll 4
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
if (e[i] != rhs.e[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool operator!=(const vec<N>& rhs) noexcept {
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr vec2& operator=(const vec2& other) noexcept {
|
||||||
|
vec2::operator=(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
constexpr vec2& operator=(const vec<2>& other) noexcept {
|
||||||
|
vec::operator=(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr vec3& operator=(const vec<3>& other) noexcept {
|
||||||
|
vec::operator=(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr vec4& operator=(const vec<4>& other) noexcept {
|
||||||
|
vec::operator=(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename STREAM_TYPE, size_t N>
|
||||||
|
STREAM_TYPE& operator<<(STREAM_TYPE& lhs, const vec<N>& rhs) {
|
||||||
|
lhs << '<';
|
||||||
|
|
||||||
|
for (size_t i = 0; i < N; ++i) {
|
||||||
|
lhs << rhs.e[i];
|
||||||
|
|
||||||
|
if (i < N - 1) {
|
||||||
|
lhs << ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs << '>';
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mtl
|
||||||
|
|
||||||
314
include/mtl/vector.hpp
Normal file
314
include/mtl/vector.hpp
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
#include <mtl/exception.hpp>
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Resizable statically allocated array
|
||||||
|
*
|
||||||
|
* Implements most functions from std::vector, see std::vector documentation
|
||||||
|
* for function descriptions. Some operations are unimplemented due to the lack
|
||||||
|
* of dynamic memory usage
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class ivector {
|
||||||
|
private:
|
||||||
|
T* m_arr;
|
||||||
|
const size_t m_capacity;
|
||||||
|
size_t m_size;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
using size_type = size_t;
|
||||||
|
using difference_type = ptrdiff_t;
|
||||||
|
using reference = T&;
|
||||||
|
using const_reference = const T&;
|
||||||
|
using pointer = T*;
|
||||||
|
using const_pointer = const T*;
|
||||||
|
using iterator = T*;
|
||||||
|
using const_iterator = const T*;
|
||||||
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||||
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||||
|
|
||||||
|
ivector(T* arr, size_t capacity) : m_arr(arr), m_capacity(capacity), m_size(0) {}
|
||||||
|
/**
|
||||||
|
* \brief Copy constructor
|
||||||
|
*
|
||||||
|
* \exception mtl::length_error Thrown if the source vector's size is
|
||||||
|
* larger than the new capacity
|
||||||
|
*/
|
||||||
|
ivector(T* arr, size_t capacity, const ivector& other) : m_arr(arr), m_capacity(capacity) {
|
||||||
|
if (&other == this) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other.m_size > m_capacity) {
|
||||||
|
throw mtl::length_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
T* x = m_arr;
|
||||||
|
T* begin = other.m_arr;
|
||||||
|
T* end = other.m_arr + other.m_size;
|
||||||
|
for (; begin != end; ++x, ++begin) {
|
||||||
|
*x = *begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_size = other.m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Move constructor
|
||||||
|
*
|
||||||
|
* \exception mtl::length_error Thrown if the source vector's size is
|
||||||
|
* larger than the new capacity
|
||||||
|
*/
|
||||||
|
ivector(ivector&& other) {
|
||||||
|
if (&other == this) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other.m_size > m_capacity) {
|
||||||
|
throw mtl::length_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're not able to simply swap pointers because dynamic memory
|
||||||
|
// is not used. However, we can still swap each individual
|
||||||
|
// element if possible.
|
||||||
|
T* x = m_arr;
|
||||||
|
T* begin = other.m_arr;
|
||||||
|
T* end = other.m_arr + other.m_size;
|
||||||
|
for (; begin != end; ++x, ++begin) {
|
||||||
|
*x = std::move(*begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_size = other.m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ivector() {}
|
||||||
|
|
||||||
|
ivector& operator=(ivector rhs) {
|
||||||
|
throw mtl::system_error();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void assign(size_t count, const T& value) {
|
||||||
|
throw mtl::system_error();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* \brief Assigns values to the vector
|
||||||
|
*
|
||||||
|
* \exception mtl::length_error Thrown if the number of source elements
|
||||||
|
* is larger than the capacity
|
||||||
|
*/
|
||||||
|
template <typename It>
|
||||||
|
void assign(It begin, It end) {
|
||||||
|
m_size = 0;
|
||||||
|
T* x = m_arr;
|
||||||
|
|
||||||
|
for (; begin != end; ++begin, ++x, ++m_size) {
|
||||||
|
if (m_size > m_capacity) {
|
||||||
|
//throw mtl::length_error();
|
||||||
|
}
|
||||||
|
*x = *begin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Bounds-checked element access
|
||||||
|
*
|
||||||
|
* \exception mtl::out_of_range Thrown if pos > size
|
||||||
|
*/
|
||||||
|
reference at(size_t pos) {
|
||||||
|
if (pos > m_size) {
|
||||||
|
throw mtl::out_of_range();
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_arr[pos];
|
||||||
|
}
|
||||||
|
const_reference at(size_t pos) const {
|
||||||
|
if (pos > m_size) {
|
||||||
|
throw mtl::out_of_range();
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_arr[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
reference operator[](size_t pos) {
|
||||||
|
return m_arr[pos];
|
||||||
|
}
|
||||||
|
const_reference operator[](size_t pos) const {
|
||||||
|
return m_arr[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
reference front() {
|
||||||
|
return m_arr[0];
|
||||||
|
}
|
||||||
|
const_reference front() const {
|
||||||
|
return m_arr[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
reference back() {
|
||||||
|
return m_arr[m_size - 1];
|
||||||
|
}
|
||||||
|
const_reference back() const {
|
||||||
|
return m_arr[m_size - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer data() {
|
||||||
|
return m_arr;
|
||||||
|
}
|
||||||
|
const_pointer data() const {
|
||||||
|
return m_arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator begin() {
|
||||||
|
return m_arr;
|
||||||
|
}
|
||||||
|
const_iterator begin() const {
|
||||||
|
return m_arr;
|
||||||
|
}
|
||||||
|
const_iterator cbegin() const {
|
||||||
|
return m_arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator end() {
|
||||||
|
return m_arr + m_size;
|
||||||
|
}
|
||||||
|
const_iterator end() const {
|
||||||
|
return m_arr + m_size;
|
||||||
|
}
|
||||||
|
const_iterator cend() const {
|
||||||
|
return m_arr + m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse_iterator rbegin() {
|
||||||
|
return reverse_iterator(end());
|
||||||
|
}
|
||||||
|
const_reverse_iterator rbegin() const {
|
||||||
|
return const_reverse_iterator(cend());
|
||||||
|
}
|
||||||
|
const_reverse_iterator crbegin() const {
|
||||||
|
return const_reverse_iterator(cend());
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse_iterator rend() {
|
||||||
|
return reverse_iterator(begin());
|
||||||
|
}
|
||||||
|
const_reverse_iterator rend() const {
|
||||||
|
return const_reverse_iterator(cbegin());
|
||||||
|
}
|
||||||
|
const_reverse_iterator crend() const {
|
||||||
|
return const_reverse_iterator(cbegin());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const {
|
||||||
|
return m_size == 0;
|
||||||
|
}
|
||||||
|
size_t size() const {
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
size_t capacity() const {
|
||||||
|
return m_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator insert(const_iterator pos, T value) {
|
||||||
|
if (m_size == m_capacity) {
|
||||||
|
throw mtl::length_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator it = end();
|
||||||
|
while (it != pos) {
|
||||||
|
*it = *(it - 1);
|
||||||
|
--it;
|
||||||
|
}
|
||||||
|
|
||||||
|
*it = std::move(value);
|
||||||
|
++m_size;
|
||||||
|
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Erase the element at the given iterator
|
||||||
|
*
|
||||||
|
* \param pos iterator of the element to erase
|
||||||
|
* \exception nothrow if `T` is nothrow assignable
|
||||||
|
* \returns iterator of the element after element erased
|
||||||
|
*/
|
||||||
|
iterator erase(const_iterator pos) noexcept(std::is_nothrow_assignable<T, T>::value) {
|
||||||
|
iterator it = const_cast<iterator>(pos);
|
||||||
|
iterator last = end() - 1;
|
||||||
|
|
||||||
|
while (it != last) {
|
||||||
|
*it = *(it + 1);
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
--m_size;
|
||||||
|
|
||||||
|
return const_cast<iterator>(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_back(T value) {
|
||||||
|
if (m_size == m_capacity) {
|
||||||
|
throw mtl::length_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_arr[m_size] = std::move(value);
|
||||||
|
++m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_back() noexcept {
|
||||||
|
if (m_size > 0) {
|
||||||
|
--m_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, size_t C>
|
||||||
|
class vector : public ivector<T> {
|
||||||
|
private:
|
||||||
|
T m_arr[C];
|
||||||
|
|
||||||
|
public:
|
||||||
|
vector() : ivector<T>(m_arr, C) { }
|
||||||
|
|
||||||
|
vector(const ivector<T>& other) {
|
||||||
|
if (other.size() > C) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->assign(other.begin(), other.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
vector(const vector<T, C>& other) : ivector<T>(m_arr, C) {
|
||||||
|
if (other.size() > C) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->assign(other.begin(), other.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<T, C>& operator=(const ivector<T>& rhs) {
|
||||||
|
this->assign(rhs.begin(), rhs.end());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: FIX BAD ASSIGNMENT OPERATORS
|
||||||
|
vector<T, C>& operator=(const vector<T, C>& rhs) {
|
||||||
|
this->assign(rhs.begin(), rhs.end());
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mtl
|
||||||
112
include/test/mtl/test.hpp
Normal file
112
include/test/mtl/test.hpp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __GBA__
|
||||||
|
|
||||||
|
#ifndef REG_TM2D
|
||||||
|
#define REG_TM2D *(volatile uint16_t*)(0x04000108)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef REG_TM2CNT
|
||||||
|
#define REG_TM2CNT *(volatile uint16_t*)(0x0400010A)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TM_ENABLE
|
||||||
|
#define TM_ENABLE 0x80
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "mtl/log.hpp"
|
||||||
|
#include "mtl/string_view.hpp"
|
||||||
|
#include "mtl/vector.hpp"
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
template <typename SUITE_TYPE>
|
||||||
|
class suite {
|
||||||
|
public:
|
||||||
|
using TEST_TYPE = bool (*)();
|
||||||
|
|
||||||
|
vector<TEST_TYPE, 256> m_tests;
|
||||||
|
vector<string_view, 256> m_test_names;
|
||||||
|
|
||||||
|
virtual string_view name() = 0;
|
||||||
|
|
||||||
|
void add_test(TEST_TYPE test, const string_view& name) {
|
||||||
|
m_tests.push_back(test);
|
||||||
|
m_test_names.push_back(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset_timer() {
|
||||||
|
#ifdef __GBA__
|
||||||
|
REG_TM2D = UINT16_MAX;
|
||||||
|
// We must enable and disable the timer to write UINT16_MAX
|
||||||
|
// to the timer register
|
||||||
|
REG_TM2CNT = TM_ENABLE;
|
||||||
|
REG_TM2CNT &= ~TM_ENABLE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start_timer() {
|
||||||
|
#ifdef __GBA__
|
||||||
|
REG_TM2D = 0;
|
||||||
|
REG_TM2CNT = TM_ENABLE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void end_timer() {
|
||||||
|
#ifdef __GBA__
|
||||||
|
REG_TM2CNT &= ~TM_ENABLE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t query_timer() {
|
||||||
|
#ifdef __GBA__
|
||||||
|
return REG_TM2D;
|
||||||
|
#endif
|
||||||
|
return UINT16_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool run_tests() {
|
||||||
|
log::info << "=========================" << endl;
|
||||||
|
log::info << "Running suite \"" << instance().name() << '\"' << endl;
|
||||||
|
log::info << endl;
|
||||||
|
|
||||||
|
size_t num_ok = 0;
|
||||||
|
size_t num_fail = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < instance().m_tests.size(); ++i) {
|
||||||
|
TEST_TYPE test = instance().m_tests[i];
|
||||||
|
string_view name = instance().m_test_names[i];
|
||||||
|
|
||||||
|
log::info << "Running \"" << name << "\"..." << endl;
|
||||||
|
|
||||||
|
reset_timer();
|
||||||
|
bool result = test();
|
||||||
|
|
||||||
|
log::info << (result ? "OK" : "FAILED") << ", TIME: " << query_timer() << endl;
|
||||||
|
|
||||||
|
if (result) { ++num_ok; }
|
||||||
|
else { ++num_fail; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
log::info << endl;
|
||||||
|
log::info << "Finished. Tests OK: " << num_ok << '/' << num_ok + num_fail << endl;
|
||||||
|
log::info << (num_fail == 0 ? "ALL OK" : "FAILED") << endl;
|
||||||
|
log::info << "=========================" << endl;
|
||||||
|
|
||||||
|
return num_fail == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SUITE_TYPE& instance() {
|
||||||
|
static SUITE_TYPE instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
|
||||||
|
} // namespace mtl
|
||||||
44
include/test/mtl/test/fixed.hpp
Normal file
44
include/test/mtl/test/fixed.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "mtl/test.hpp"
|
||||||
|
|
||||||
|
#include "mtl/fixed.hpp"
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
class fixed_suite : public suite<fixed_suite> {
|
||||||
|
public:
|
||||||
|
fixed_suite() {
|
||||||
|
add_test(&construction, "construction");
|
||||||
|
add_test(&addition, "addition");
|
||||||
|
add_test(&subtraction, "subtraction");
|
||||||
|
add_test(&multiplication, "multiplication");
|
||||||
|
add_test(&mult_overflow, "mult_overflow");
|
||||||
|
add_test(&division, "division");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual string_view name() {
|
||||||
|
return "fixed_suite";
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool construction() {
|
||||||
|
start_timer();
|
||||||
|
bool r = fixed(7).raw() == 7 * 64;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All tests are placed in IWRAM in ARM mode.
|
||||||
|
static bool addition();
|
||||||
|
static bool subtraction();
|
||||||
|
static bool multiplication();
|
||||||
|
static bool mult_overflow();
|
||||||
|
static bool division();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
|
||||||
|
} // namespace mtl
|
||||||
|
|
||||||
64
include/test/mtl/test/mat.hpp
Normal file
64
include/test/mtl/test/mat.hpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "mtl/test.hpp"
|
||||||
|
#include "mtl/target.hpp"
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
class mat_suite : public suite<mat_suite> {
|
||||||
|
public:
|
||||||
|
mat_suite() {
|
||||||
|
add_test(&construction_m2, "construction_m2");
|
||||||
|
|
||||||
|
add_test(&addition_m2, "addition_m2");
|
||||||
|
add_test(&addition_m3, "addition_m3");
|
||||||
|
add_test(&addition_m4, "addition_m4");
|
||||||
|
|
||||||
|
add_test(&subtraction_m2, "subtraction_m2");
|
||||||
|
add_test(&subtraction_m3, "subtraction_m3");
|
||||||
|
add_test(&subtraction_m4, "subtraction_m4");
|
||||||
|
|
||||||
|
add_test(&mult_vec_m2, "mult_vec_m2");
|
||||||
|
add_test(&mult_vec_m3, "mult_vec_m3");
|
||||||
|
add_test(&mult_vec_m4, "mult_vec_m4");
|
||||||
|
|
||||||
|
add_test(&mult_mat_m2, "mult_mat_m2");
|
||||||
|
add_test(&mult_mat_m3, "mult_mat_m3");
|
||||||
|
add_test(&mult_mat_m4, "mult_mat_m4");
|
||||||
|
|
||||||
|
add_test(&projection_build_m4, "projection_build_m4");
|
||||||
|
add_test(&projection_calc_m4, "projection_calc_m4");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual string_view name() {
|
||||||
|
return "mat_suite";
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool construction_m2();
|
||||||
|
|
||||||
|
static bool addition_m2();
|
||||||
|
static bool addition_m3();
|
||||||
|
static bool addition_m4();
|
||||||
|
|
||||||
|
static bool subtraction_m2();
|
||||||
|
static bool subtraction_m3();
|
||||||
|
static bool subtraction_m4();
|
||||||
|
|
||||||
|
static bool mult_vec_m2();
|
||||||
|
static bool mult_vec_m3();
|
||||||
|
static bool mult_vec_m4();
|
||||||
|
|
||||||
|
static bool mult_mat_m2();
|
||||||
|
static bool mult_mat_m3();
|
||||||
|
static bool mult_mat_m4();
|
||||||
|
|
||||||
|
static bool projection_build_m4();
|
||||||
|
static bool projection_calc_m4();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
|
||||||
|
} // namespace mtl
|
||||||
|
|
||||||
104
include/test/mtl/test/vec.hpp
Normal file
104
include/test/mtl/test/vec.hpp
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#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");
|
||||||
|
|
||||||
|
add_test(&projection_v4, "projection_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool addition_v2();
|
||||||
|
static bool addition_v3();
|
||||||
|
static bool addition_v4();
|
||||||
|
|
||||||
|
static bool subtraction_v2();
|
||||||
|
static bool subtraction_v3();
|
||||||
|
static bool subtraction_v4();
|
||||||
|
|
||||||
|
static bool negation_v2();
|
||||||
|
static bool negation_v3();
|
||||||
|
static bool negation_v4();
|
||||||
|
|
||||||
|
static bool mult_scalar_v2();
|
||||||
|
static bool mult_scalar_v3();
|
||||||
|
static bool mult_scalar_v4();
|
||||||
|
|
||||||
|
static bool dot_v2();
|
||||||
|
static bool dot_v3();
|
||||||
|
static bool dot_v4();
|
||||||
|
|
||||||
|
static bool division_scalar_v2();
|
||||||
|
static bool division_scalar_v3();
|
||||||
|
static bool division_scalar_v4();
|
||||||
|
|
||||||
|
static bool magnitude_sqr_v2();
|
||||||
|
static bool magnitude_sqr_v3();
|
||||||
|
static bool magnitude_sqr_v4();
|
||||||
|
|
||||||
|
static bool transpose_v2();
|
||||||
|
static bool transpose_v3();
|
||||||
|
static bool transpose_v4();
|
||||||
|
|
||||||
|
static bool projection_v4();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
|
||||||
|
} // namespace mtl
|
||||||
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
.section .iwram, "ax", %progbits
|
|
||||||
.arm
|
|
||||||
.align 2
|
|
||||||
|
|
||||||
.global mtl_fixed_mul
|
|
||||||
.type mtl_fixed_mul STT_FUNC
|
|
||||||
mtl_fixed_mul:
|
|
||||||
smull r2, r3, r0, r1
|
|
||||||
lsr r0, r2, #6
|
|
||||||
orr r0, r3, lsl #26
|
|
||||||
bx lr
|
|
||||||
8
src/common/mat_1x2.cpp
Normal file
8
src/common/mat_1x2.cpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include "mtl/mat_impl.hpp"
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
|
||||||
|
template class mat<1, 2>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
8
src/common/mat_1x3.cpp
Normal file
8
src/common/mat_1x3.cpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include "mtl/mat_impl.hpp"
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
|
||||||
|
template class mat<1, 3>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
8
src/common/mat_1x4.cpp
Normal file
8
src/common/mat_1x4.cpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include "mtl/mat_impl.hpp"
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
|
||||||
|
template class mat<1, 4>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
11
src/common/mat_2x2.cpp
Normal file
11
src/common/mat_2x2.cpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "mtl/mat_impl.hpp"
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
|
||||||
|
template class mat<2, 2>;
|
||||||
|
|
||||||
|
template mat<2, 2> operator*(const vec<2>&, const mat<1, 2>&);
|
||||||
|
template mat<2, 2> mat<2, 2>::operator*(const mat<2, 2>&) const;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
11
src/common/mat_3x3.cpp
Normal file
11
src/common/mat_3x3.cpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "mtl/mat_impl.hpp"
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
|
||||||
|
template class mat<3, 3>;
|
||||||
|
|
||||||
|
template mat<3, 3> operator*(const vec<3>&, const mat<1, 3>&);
|
||||||
|
template mat<3, 3> mat<3, 3>::operator*(const mat<3, 3>&) const;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
12
src/common/mat_4x4.cpp
Normal file
12
src/common/mat_4x4.cpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include "mtl/mat_impl.hpp"
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
|
||||||
|
template class mat<4, 4>;
|
||||||
|
|
||||||
|
template mat<4, 4> operator*(const vec<4>&, const mat<1, 4>&);
|
||||||
|
template mat<4, 4> mat<4, 4>::operator*(const mat<4, 4>&) const;
|
||||||
|
|
||||||
|
template mat<4, 4> mat<4, 4>::translation<4, true>(const vec<3>&);
|
||||||
|
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "mtl/utility.hpp"
|
#include "mtl/utility.hpp"
|
||||||
#include "mtl/string_view.hpp"
|
#include "mtl/string_view.hpp"
|
||||||
@ -18,4 +18,11 @@ size_t string_view::copy(char* dest, size_t count, size_t pos) const {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool string_view::operator==(const string_view& rhs) const {
|
||||||
|
return strcmp(m_str, rhs.m_str) == 0;
|
||||||
|
}
|
||||||
|
bool string_view::operator!=(const string_view& rhs) const {
|
||||||
|
return strcmp(m_str, rhs.m_str) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mtl
|
} // namespace mtl
|
||||||
@ -1,37 +0,0 @@
|
|||||||
#include "mtl/fiber.hpp"
|
|
||||||
|
|
||||||
namespace mtl {
|
|
||||||
|
|
||||||
void ifiber_dependency::clear() {
|
|
||||||
for (ifiber* dependent : m_dependents) {
|
|
||||||
--dependent->m_dependencies;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_dependents.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ifiber_dependency::add_dependent(ifiber* dependent) {
|
|
||||||
m_dependents.push_back(dependent);
|
|
||||||
++dependent->m_dependencies;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*ifiber::ifiber(priority priority)
|
|
||||||
: m_priority(priority), m_finished(false), m_dependencies(0) {
|
|
||||||
switch (priority) {
|
|
||||||
case priority::low:
|
|
||||||
if (s_queue_low.size() < s_queue_low.capacity()) {
|
|
||||||
s_queue_low.push_back(this);
|
|
||||||
} else {
|
|
||||||
mtl::yield();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case priority::med:
|
|
||||||
s_queue_med.push_back(this);
|
|
||||||
break;
|
|
||||||
case priority::hi:
|
|
||||||
s_queue_hi.push_back(this);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
} // namespace mtl
|
|
||||||
55
src/gba/fixed.cpp
Normal file
55
src/gba/fixed.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#include "mtl/fixed.hpp"
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
|
||||||
|
fixed fixed::operator/(fixed rhs) const noexcept {
|
||||||
|
int32_t raw_result;
|
||||||
|
asm(
|
||||||
|
// This division implementation has two methods it can use.
|
||||||
|
// The fastest uses a left shift followed by a single division. The value is shifted
|
||||||
|
// first to preserve the decimal part. Unfortunately, this means large numerators
|
||||||
|
// will cause the operation to overflow. In this case, a compatible method will be
|
||||||
|
// used. This method uses two divisions, one to calculate the integral quotient,
|
||||||
|
// and one to calculate the decimal part. Both these methods work for negative numbers as well.
|
||||||
|
".arm;"
|
||||||
|
"movs r1, %[d];" // Load numerator and denominator, and check if negative or zero
|
||||||
|
"beq 4f;"
|
||||||
|
"movs r0, %[n];"
|
||||||
|
"ldr r3, =#0x7f800000;" // Load constant to check for overflow
|
||||||
|
"blt 1f;"
|
||||||
|
"tst r0, r3;" // Check if the numerator is large enough to overflow from the leftshift
|
||||||
|
"bne 3f;"
|
||||||
|
"b 2f;"
|
||||||
|
"1:" // check_negative
|
||||||
|
"mvn r2, r0;" // Check if the numerator is large enough to overflow from the leftshift
|
||||||
|
"tst r2, r3;"
|
||||||
|
"bne 3f;"
|
||||||
|
"2:" // fast_div // Fast method
|
||||||
|
"lsl r0, #8;" // Shift first to avoid truncation
|
||||||
|
"swi #0x60000;" // GBA Div syscall
|
||||||
|
"mov %[res], r0;"
|
||||||
|
"b 5f;"
|
||||||
|
"3:" // compat_div // Compatible method
|
||||||
|
"swi #0x60000;" // Compute quotient and shift
|
||||||
|
"lsl r2, r0, #8;"
|
||||||
|
"mov r0, r1;" // Div syscall puts the modulus in r1, use it as the numerator
|
||||||
|
"lsr r1, %[d], #8;" // Load the denominator again, shifted right to calculate decimal part
|
||||||
|
"swi #0x60000;"
|
||||||
|
"mov %[res], r2;" // Calculate the final result
|
||||||
|
"add %[res], r0;"
|
||||||
|
"b 5f;"
|
||||||
|
"4:" // zero_div
|
||||||
|
"teq %[n], %[d];" // Set result to largest possible negative/positive value.
|
||||||
|
"movmi %[res], #0x80000000;"
|
||||||
|
"movpl %[res], #0x7FFFFFFF;"
|
||||||
|
"5:"
|
||||||
|
: [res] "=r" (raw_result)
|
||||||
|
: [n] "r" (x),
|
||||||
|
[d] "r" (rhs.x)
|
||||||
|
: "r0", "r1", "r2", "r3"
|
||||||
|
);
|
||||||
|
return from_raw(raw_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mtl
|
||||||
|
|
||||||
84
src/test/fixed.cpp
Normal file
84
src/test/fixed.cpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#include "mtl/test/fixed.hpp"
|
||||||
|
|
||||||
|
#include "mtl/target.hpp"
|
||||||
|
#include "mtl/fixed.hpp"
|
||||||
|
|
||||||
|
TARGET_ARM_MODE
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
namespace test{
|
||||||
|
|
||||||
|
bool fixed_suite::addition() {
|
||||||
|
struct {
|
||||||
|
NOINLINE fixed operator()(fixed x, fixed y) {
|
||||||
|
start_timer();
|
||||||
|
fixed r = x + y;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
return f(3, 4) == fixed(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fixed_suite::subtraction() {
|
||||||
|
struct {
|
||||||
|
NOINLINE fixed operator()(fixed x, fixed y) {
|
||||||
|
start_timer();
|
||||||
|
fixed r = x - y;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
return f(8, 3) == fixed(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fixed_suite::multiplication() {
|
||||||
|
struct {
|
||||||
|
NOINLINE fixed operator()(fixed x, fixed y) {
|
||||||
|
start_timer();
|
||||||
|
fixed r = x * y;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
return f(3, 4) == fixed(12);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fixed_suite::mult_overflow() {
|
||||||
|
// This tests fixed point multiplication where the intermediate result
|
||||||
|
// (x.raw * y.raw) overflows int32 max still works. (IE. int64 multiplication
|
||||||
|
// is used internally)
|
||||||
|
struct {
|
||||||
|
NOINLINE fixed operator()(fixed x, fixed y) {
|
||||||
|
start_timer();
|
||||||
|
fixed r = x * y;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
return f(1048575, 8) == fixed(8388600);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fixed_suite::division() {
|
||||||
|
struct {
|
||||||
|
NOINLINE fixed operator()(fixed x, fixed y) {
|
||||||
|
start_timer();
|
||||||
|
fixed r = x / y;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
return f(73, 13) == fixed(73.0 / 13.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TARGET_END_MODE
|
||||||
|
|
||||||
446
src/test/mat.cpp
Normal file
446
src/test/mat.cpp
Normal file
@ -0,0 +1,446 @@
|
|||||||
|
#include "mtl/test/mat.hpp"
|
||||||
|
|
||||||
|
#include "mtl/target.hpp"
|
||||||
|
#include "mtl/mat.hpp"
|
||||||
|
#include "mtl/vec.hpp"
|
||||||
|
|
||||||
|
namespace mtl {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
bool mat_suite::construction_m2() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
mat<2, 2> operator()(fixed a, fixed b, fixed c, fixed d) {
|
||||||
|
start_timer();
|
||||||
|
mat<2, 2> r({
|
||||||
|
{ a, b },
|
||||||
|
{ c, d },
|
||||||
|
});
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
mat<2, 2> a = f(1, 2, 3, 4);
|
||||||
|
|
||||||
|
return a.row(0)[0] == 1 && a.row(0)[1] == 2 &&
|
||||||
|
a.row(1)[0] == 3 && a.row(1)[1] == 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mat_suite::addition_m2() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
mat<2, 2> operator()(mat<2, 2> a, mat<2, 2> b) {
|
||||||
|
start_timer();
|
||||||
|
mat<2, 2> r = a + b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
mat<2, 2> a({
|
||||||
|
{ 1, 2 },
|
||||||
|
{ 3, 4 },
|
||||||
|
});
|
||||||
|
mat<2, 2> b({
|
||||||
|
{ 10, 20 },
|
||||||
|
{ 30, 40 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<2, 2> c = f(a, b);
|
||||||
|
|
||||||
|
mat<2, 2> exp({
|
||||||
|
{ 11, 22 },
|
||||||
|
{ 33, 44 },
|
||||||
|
});
|
||||||
|
|
||||||
|
return c == exp;
|
||||||
|
}
|
||||||
|
bool mat_suite::addition_m3() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
mat<3, 3> operator()(mat<3, 3> a, mat<3, 3> b) {
|
||||||
|
start_timer();
|
||||||
|
mat<3, 3> r = a + b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
mat<3, 3> a({
|
||||||
|
{ 1, 2, 3 },
|
||||||
|
{ 4, 5, 6 },
|
||||||
|
{ 7, 8, 9 },
|
||||||
|
});
|
||||||
|
mat<3, 3> b({
|
||||||
|
{ 10, 20, 30 },
|
||||||
|
{ 40, 50, 60 },
|
||||||
|
{ 70, 80, 90 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<3, 3> c = f(a, b);
|
||||||
|
|
||||||
|
mat<3, 3> exp({
|
||||||
|
{ 11, 22, 33 },
|
||||||
|
{ 44, 55, 66 },
|
||||||
|
{ 77, 88, 99 },
|
||||||
|
});
|
||||||
|
|
||||||
|
return c == exp;
|
||||||
|
}
|
||||||
|
bool mat_suite::addition_m4() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
mat<4, 4> operator()(mat<4, 4> a, mat<4, 4> b) {
|
||||||
|
start_timer();
|
||||||
|
mat<4, 4> r = a + b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
mat<4, 4> a({
|
||||||
|
{ 1, 2, 3, 4 },
|
||||||
|
{ 5, 6, 7, 8 },
|
||||||
|
{ 9, 1, 2, 3 },
|
||||||
|
{ 4, 5, 6, 7 },
|
||||||
|
});
|
||||||
|
mat<4, 4> b({
|
||||||
|
{ 10, 20, 30, 40 },
|
||||||
|
{ 50, 60, 70, 80 },
|
||||||
|
{ 90, 10, 20, 30 },
|
||||||
|
{ 40, 50, 60, 70 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<4, 4> c = f(a, b);
|
||||||
|
|
||||||
|
mat<4, 4> exp({
|
||||||
|
{ 11, 22, 33, 44 },
|
||||||
|
{ 55, 66, 77, 88 },
|
||||||
|
{ 99, 11, 22, 33 },
|
||||||
|
{ 44, 55, 66, 77 },
|
||||||
|
});
|
||||||
|
|
||||||
|
return c == exp;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mat_suite::subtraction_m2() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
mat<2, 2> operator()(mat<2, 2> a, mat<2, 2> b) {
|
||||||
|
start_timer();
|
||||||
|
mat<2, 2> r = a - b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
mat<2, 2> a({
|
||||||
|
{ 11, 22 },
|
||||||
|
{ 33, 44 },
|
||||||
|
});
|
||||||
|
mat<2, 2> b({
|
||||||
|
{ 1, 2 },
|
||||||
|
{ 3, 4 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<2, 2> c = f(a, b);
|
||||||
|
|
||||||
|
mat<2, 2> exp({
|
||||||
|
{ 10, 20 },
|
||||||
|
{ 30, 40 },
|
||||||
|
});
|
||||||
|
|
||||||
|
return c == exp;
|
||||||
|
}
|
||||||
|
bool mat_suite::subtraction_m3() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
mat<3, 3> operator()(mat<3, 3> a, mat<3, 3> b) {
|
||||||
|
start_timer();
|
||||||
|
mat<3, 3> r = a - b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
mat<3, 3> a({
|
||||||
|
{ 11, 22, 33 },
|
||||||
|
{ 44, 55, 66 },
|
||||||
|
{ 77, 88, 99 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<3, 3> b({
|
||||||
|
{ 1, 2, 3 },
|
||||||
|
{ 4, 5, 6 },
|
||||||
|
{ 7, 8, 9 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<3, 3> c = f(a, b);
|
||||||
|
|
||||||
|
mat<3, 3> exp({
|
||||||
|
{ 10, 20, 30 },
|
||||||
|
{ 40, 50, 60 },
|
||||||
|
{ 70, 80, 90 },
|
||||||
|
});
|
||||||
|
|
||||||
|
return c == exp;
|
||||||
|
}
|
||||||
|
bool mat_suite::subtraction_m4() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
mat<4, 4> operator()(mat<4, 4> a, mat<4, 4> b) {
|
||||||
|
start_timer();
|
||||||
|
mat<4, 4> r = a - b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
mat<4, 4> a({
|
||||||
|
{ 11, 22, 33, 44 },
|
||||||
|
{ 55, 66, 77, 88 },
|
||||||
|
{ 99, 11, 22, 33 },
|
||||||
|
{ 44, 55, 66, 77 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<4, 4> b({
|
||||||
|
{ 1, 2, 3, 4 },
|
||||||
|
{ 5, 6, 7, 8 },
|
||||||
|
{ 9, 1, 2, 3 },
|
||||||
|
{ 4, 5, 6, 7 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<4, 4> c = f(a, b);
|
||||||
|
|
||||||
|
mat<4, 4> exp({
|
||||||
|
{ 10, 20, 30, 40 },
|
||||||
|
{ 50, 60, 70, 80 },
|
||||||
|
{ 90, 10, 20, 30 },
|
||||||
|
{ 40, 50, 60, 70 },
|
||||||
|
});
|
||||||
|
|
||||||
|
return c == exp;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mat_suite::mult_vec_m2() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
vec2 operator()(mat<2, 2> a, vec2 b) {
|
||||||
|
start_timer();
|
||||||
|
vec2 r = a * b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
mat<2, 2> a({
|
||||||
|
{ 1, 2 }, // col 1
|
||||||
|
{ 3, 4 } // col 2
|
||||||
|
});
|
||||||
|
|
||||||
|
vec2 b(1, 2);
|
||||||
|
|
||||||
|
vec2 c = f(a, b);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool mat_suite::mult_vec_m3() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
vec3 operator()(mat<3, 3> a, vec3 b) {
|
||||||
|
start_timer();
|
||||||
|
vec3 r = a * b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
mat<3, 3> a({
|
||||||
|
{ 1, 2, 3 }, // col 1
|
||||||
|
{ 4, 5, 6 }, // col 2
|
||||||
|
{ 7, 8, 9 }, // col 3
|
||||||
|
});
|
||||||
|
|
||||||
|
vec3 b(1, 2, 3);
|
||||||
|
|
||||||
|
f(a, b);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool mat_suite::mult_vec_m4() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
vec4 operator()(mat<4, 4> a, vec4 b) {
|
||||||
|
start_timer();
|
||||||
|
vec4 r = a * b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
mat<4, 4> a({
|
||||||
|
{ 1, 2, 3, 4 }, // col 1
|
||||||
|
{ 5, 6, 7, 8 }, // col 2
|
||||||
|
{ 9, 1, 2, 3 }, // col 3
|
||||||
|
{ 4, 5, 6, 7 } // col 4
|
||||||
|
});
|
||||||
|
|
||||||
|
vec4 b(1, 2, 3, 4);
|
||||||
|
|
||||||
|
f(a, b);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mat_suite::mult_mat_m2() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
mat<2, 2> operator()(mat<2, 2> a, mat<2, 2> b) {
|
||||||
|
start_timer();
|
||||||
|
mat<2, 2> r = a * b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
mat<2, 2> a({
|
||||||
|
{ 1, 2 },
|
||||||
|
{ 3, 4 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<2, 2> b({
|
||||||
|
{ 11, 22 },
|
||||||
|
{ 33, 44 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<2, 2> exp({
|
||||||
|
{ 77, 110 },
|
||||||
|
{ 165, 242 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<2, 2> c = f(a, b);
|
||||||
|
|
||||||
|
return c == exp;
|
||||||
|
}
|
||||||
|
bool mat_suite::mult_mat_m3() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
mat<3, 3> operator()(mat<3, 3> a, mat<3, 3> b) {
|
||||||
|
start_timer();
|
||||||
|
mat<3, 3> r = a * b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
mat<3, 3> a({
|
||||||
|
{ 1, 2, 3 },
|
||||||
|
{ 4, 5, 6 },
|
||||||
|
{ 7, 8, 9 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<3, 3> b({
|
||||||
|
{ 11, 22, 33 },
|
||||||
|
{ 44, 55, 66 },
|
||||||
|
{ 77, 88, 99 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<3, 3> exp({
|
||||||
|
{ 330, 396, 462 },
|
||||||
|
{ 726, 891, 1056 },
|
||||||
|
{ 1122, 1386, 1650 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<3, 3> c = f(a, b);
|
||||||
|
|
||||||
|
return c == exp;
|
||||||
|
}
|
||||||
|
bool mat_suite::mult_mat_m4() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
mat<4, 4> operator()(mat<4, 4> a, mat<4, 4> b) {
|
||||||
|
start_timer();
|
||||||
|
mat<4, 4> r = a * b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
mat<4, 4> a({
|
||||||
|
{ 1, 2, 3, 4 },
|
||||||
|
{ 5, 6, 7, 8 },
|
||||||
|
{ 9, 1, 2, 3 },
|
||||||
|
{ 4, 5, 6, 7 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<4, 4> b({
|
||||||
|
{ 11, 22, 33, 44 },
|
||||||
|
{ 55, 66, 77, 88 },
|
||||||
|
{ 99, 11, 22, 33 },
|
||||||
|
{ 44, 55, 66, 77 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<4, 4> exp({
|
||||||
|
{ 594, 407, 517, 627 },
|
||||||
|
{ 1430, 1023, 1309, 1595 },
|
||||||
|
{ 484, 451, 616, 781 },
|
||||||
|
{ 1221, 869, 1111, 1353 },
|
||||||
|
});
|
||||||
|
|
||||||
|
mat<4, 4> c = f(a, b);
|
||||||
|
|
||||||
|
return c == exp;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mat_suite::projection_build_m4() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
mat<4, 4> operator()(vec4 b) {
|
||||||
|
start_timer();
|
||||||
|
mat<4, 4> r = b * b.transpose() * (fixed(64) / b.magnitude_sqr()) * fixed(1.0 / 64.0);
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
vec4 b(-3, 4, 14, 0);
|
||||||
|
|
||||||
|
f(b);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool mat_suite::projection_calc_m4() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
vec4 operator()(mat<4, 4> A, vec4 b) {
|
||||||
|
start_timer();
|
||||||
|
vec4 r = A * b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
vec4 a(8, 3, 7, 0);
|
||||||
|
vec4 b(-3, 4, 14, 0);
|
||||||
|
|
||||||
|
mat<4, 4> A = b * b.transpose() * (fixed(64) / b.magnitude_sqr()) * fixed(1.0 / 64.0);
|
||||||
|
|
||||||
|
vec4 c = f(A, a);
|
||||||
|
|
||||||
|
vec4 exp(fixed::from_raw(-74), fixed::from_raw(99), fixed::from_raw(348), 0);
|
||||||
|
|
||||||
|
vec4 exact_value(-1.1674, 1.5566, 5.4480, 0);
|
||||||
|
|
||||||
|
log::debug << "A = " << a << endl;
|
||||||
|
log::debug << "B = " << b << endl;
|
||||||
|
log::debug << "C = proj(A, B) = " << c << endl;
|
||||||
|
log::debug << "Exact value: " << exact_value << endl;
|
||||||
|
log::debug << "Divergence: " << exact_value - c << endl;
|
||||||
|
|
||||||
|
return c == exp;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace mtl
|
||||||
|
|
||||||
373
src/test/vec.cpp
Normal file
373
src/test/vec.cpp
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
#include "mtl/test/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) {
|
||||||
|
start_timer();
|
||||||
|
vec2 r = a + b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
vec3 r = a + b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
vec4 r = a + b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
vec2 r = a - b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
vec3 r = a - b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
vec4 r = a - b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
vec2 r = -a;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
vec3 r = -a;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
vec4 r = -a;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
vec2 r = a * x;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
vec3 r = a * x;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
vec4 r = a * x;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
fixed r = a * b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
fixed r = a * b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
fixed r = a * b;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
vec2 r = a / x;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
vec3 r = a / x;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
start_timer();
|
||||||
|
vec4 r = a / x;
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} 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() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
fixed operator()(vec2 a) {
|
||||||
|
start_timer();
|
||||||
|
fixed r = a.magnitude_sqr();
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
fixed c = f(vec2(1, 2));
|
||||||
|
|
||||||
|
return c == 5;
|
||||||
|
}
|
||||||
|
bool vec_suite::magnitude_sqr_v3() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
fixed operator()(vec3 a) {
|
||||||
|
start_timer();
|
||||||
|
fixed r = a.magnitude_sqr();
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
fixed c = f(vec3(1, 2, 3));
|
||||||
|
|
||||||
|
return c == 14;
|
||||||
|
}
|
||||||
|
bool vec_suite::magnitude_sqr_v4() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
fixed operator()(vec4 a) {
|
||||||
|
start_timer();
|
||||||
|
fixed r = a.magnitude_sqr();
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
fixed c = f(vec4(1, 2, 3, 4));
|
||||||
|
|
||||||
|
return c == 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vec_suite::projection_v4() {
|
||||||
|
struct {
|
||||||
|
NOINLINE GBA_IWRAM ARM_MODE
|
||||||
|
vec4 operator()(vec4 a, vec4 b) {
|
||||||
|
start_timer();
|
||||||
|
vec4 r = b * (a * b) / b.magnitude_sqr();
|
||||||
|
end_timer();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} f;
|
||||||
|
|
||||||
|
vec4 a(8, 3, 7, 0);
|
||||||
|
vec4 b(-3, 4, 14, 0);
|
||||||
|
|
||||||
|
vec4 c = f(a, b);
|
||||||
|
|
||||||
|
vec4 exp(fixed::from_raw(-74), fixed::from_raw(99), fixed::from_raw(348), 0);
|
||||||
|
|
||||||
|
log::debug << "A = " << a << endl;
|
||||||
|
log::debug << "B = " << b << endl;
|
||||||
|
log::debug << "C = proj(A, B) = " << c << endl;
|
||||||
|
|
||||||
|
return c == exp;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
|
||||||
|
} // namespace mtl
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user