Rewrite
This commit is contained in:
parent
7f125f03be
commit
df718217d2
36
src/tenvex/expressions/add.h
Normal file
36
src/tenvex/expressions/add.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "defines.h"
|
||||
#include "expression.h"
|
||||
#include "concepts.h"
|
||||
#include "traits.h"
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<vec_expr L, vec_expr R>
|
||||
struct Add : Expr<Add<L, R>>
|
||||
{
|
||||
TNVX_INLINE
|
||||
Add(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
|
||||
|
||||
[[nodiscard]] TNVX_INLINE
|
||||
__m128 eval() const noexcept;
|
||||
|
||||
private:
|
||||
tnvx_ref_or_value_t<L> _l;
|
||||
tnvx_ref_or_value_t<L> _r;
|
||||
};
|
||||
|
||||
template<vec_expr L, vec_expr R>
|
||||
[[nodiscard]] TNVX_INLINE
|
||||
Add<L, R> operator+(const L& l, const R& r) noexcept;
|
||||
|
||||
template<vec_expr L, vec_expr R>
|
||||
inline constexpr bool is_vec_expr<Add<L, R>> = true;
|
||||
|
||||
}
|
||||
|
||||
#include "add_impl.hpp"
|
||||
31
src/tenvex/expressions/add_impl.hpp
Normal file
31
src/tenvex/expressions/add_impl.hpp
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "defines.h"
|
||||
#include "add.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<vec_expr L, vec_expr R>
|
||||
TNVX_INLINE
|
||||
Add<L, R>::Add(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
|
||||
: _l(l)
|
||||
, _r(r)
|
||||
{
|
||||
}
|
||||
|
||||
template<vec_expr L, vec_expr R>
|
||||
[[nodiscard]] TNVX_INLINE
|
||||
__m128 Add<L, R>::eval() const noexcept
|
||||
{
|
||||
return _mm_add_ps(_l.eval(), _r.eval());
|
||||
}
|
||||
|
||||
template<vec_expr L, vec_expr R>
|
||||
[[nodiscard]] TNVX_INLINE
|
||||
Add<L, R> operator+(const L& l, const R& r) noexcept
|
||||
{
|
||||
return { l, r };
|
||||
}
|
||||
|
||||
}
|
||||
25
src/tenvex/expressions/concepts.h
Normal file
25
src/tenvex/expressions/concepts.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "traits.h"
|
||||
#include "expression.h"
|
||||
|
||||
#include <concepts>
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
concept expression = requires (T t)
|
||||
{
|
||||
{ t.eval() } -> std::same_as<__m128>;
|
||||
{ t.self() } -> std::same_as<const T&>;
|
||||
}
|
||||
&& std::is_base_of_v<Expr<T>, T>;
|
||||
|
||||
template<typename T>
|
||||
concept vec_expr = expression<T> && is_vec_expr<T>;
|
||||
|
||||
template<typename T>
|
||||
concept scalar_expr = expression<T> && is_scalar_expr<T>;
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define TNVX_INLINE __forceinline
|
||||
@ -9,4 +9,4 @@
|
||||
#else
|
||||
#define TNVX_INLINE inline
|
||||
#define TNVX_RESTRICT
|
||||
#endif
|
||||
#endif
|
||||
19
src/tenvex/expressions/expression.h
Normal file
19
src/tenvex/expressions/expression.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<typename Derived>
|
||||
struct Expr
|
||||
{
|
||||
[[nodiscard]] TNVX_INLINE
|
||||
const Derived& self() const noexcept;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "expression_impl.hpp"
|
||||
14
src/tenvex/expressions/expression_impl.hpp
Normal file
14
src/tenvex/expressions/expression_impl.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include "defines.h"
|
||||
#include "expression.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<typename Derived>
|
||||
TNVX_INLINE
|
||||
const Derived& Expr<Derived>::self() const noexcept
|
||||
{
|
||||
return static_cast<const Derived&>(*this);
|
||||
}
|
||||
|
||||
}
|
||||
48
src/tenvex/expressions/mul.h
Normal file
48
src/tenvex/expressions/mul.h
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "defines.h"
|
||||
#include "traits.h"
|
||||
#include "expression.h"
|
||||
#include "concepts.h"
|
||||
|
||||
#include "scalar.h"
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<expression L, expression R>
|
||||
struct Mul : Expr<Mul<L, R>>
|
||||
{
|
||||
TNVX_INLINE
|
||||
Mul(const L& l, const R& r) noexcept;
|
||||
|
||||
[[nodiscard]] TNVX_INLINE
|
||||
__m128 eval() const noexcept;
|
||||
|
||||
private:
|
||||
tnvx_ref_or_value_t<L> _l;
|
||||
tnvx_ref_or_value_t<R> _r;
|
||||
};
|
||||
|
||||
template<vec_expr E>
|
||||
[[nodiscard]] TNVX_INLINE
|
||||
Mul<E, Scalar> operator*(const E& l, float r) noexcept;
|
||||
|
||||
template<vec_expr E>
|
||||
[[nodiscard]] TNVX_INLINE
|
||||
Mul<Scalar, E> operator*(float l, const E& r) noexcept;
|
||||
|
||||
template<scalar_expr L, scalar_expr R>
|
||||
[[nodiscard]] TNVX_INLINE
|
||||
Mul<L, R> operator*(const L& l, const R& r) noexcept;
|
||||
|
||||
template<vec_expr L, scalar_expr R> inline constexpr bool is_vec_expr<Mul<L, R>> = true;
|
||||
template<scalar_expr L, vec_expr R> inline constexpr bool is_vec_expr<Mul<L, R>> = true;
|
||||
|
||||
template<scalar_expr L, scalar_expr R> inline constexpr bool is_scalar_expr<Mul<L, R>> = true;
|
||||
|
||||
}
|
||||
|
||||
#include "mul_impl.hpp"
|
||||
44
src/tenvex/expressions/mul_impl.hpp
Normal file
44
src/tenvex/expressions/mul_impl.hpp
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "mul.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<expression L, expression R>
|
||||
TNVX_INLINE
|
||||
Mul<L, R>::Mul(const L& l, const R& r) noexcept
|
||||
: _l(l)
|
||||
, _r(r)
|
||||
{
|
||||
}
|
||||
|
||||
template<expression L, expression R>
|
||||
TNVX_INLINE
|
||||
__m128 Mul<L, R>::eval() const noexcept
|
||||
{
|
||||
return _mm_mul_ps(_l.eval(), _r.eval());
|
||||
}
|
||||
|
||||
template<vec_expr E>
|
||||
TNVX_INLINE
|
||||
Mul<E, Scalar> operator*(const E& l, float r) noexcept
|
||||
{
|
||||
return { l, Scalar(r) };
|
||||
}
|
||||
|
||||
template<vec_expr E>
|
||||
TNVX_INLINE
|
||||
Mul<Scalar, E> operator*(float l, const E& r) noexcept
|
||||
{
|
||||
return { Scalar(l), r };
|
||||
}
|
||||
|
||||
template<scalar_expr L, scalar_expr R>
|
||||
TNVX_INLINE
|
||||
Mul<L, R> operator*(const L& l, const R& r) noexcept
|
||||
{
|
||||
return { l, r };
|
||||
}
|
||||
|
||||
}
|
||||
30
src/tenvex/expressions/scalar.h
Normal file
30
src/tenvex/expressions/scalar.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "defines.h"
|
||||
#include "expression.h"
|
||||
#include "traits.h"
|
||||
#include "concepts.h"
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
struct Scalar : Expr<Scalar>
|
||||
{
|
||||
TNVX_INLINE
|
||||
explicit Scalar(float value) noexcept;
|
||||
|
||||
[[nodiscard]] TNVX_INLINE
|
||||
__m128 eval() const noexcept;
|
||||
|
||||
private:
|
||||
__m128 _value;
|
||||
};
|
||||
|
||||
template<>
|
||||
inline constexpr bool is_scalar_expr<Scalar> = true;
|
||||
|
||||
}
|
||||
|
||||
#include "scalar_impl.hpp"
|
||||
20
src/tenvex/expressions/scalar_impl.hpp
Normal file
20
src/tenvex/expressions/scalar_impl.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "scalar.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
TNVX_INLINE
|
||||
Scalar::Scalar(float value) noexcept
|
||||
{
|
||||
_value = _mm_set1_ps(value);
|
||||
}
|
||||
|
||||
TNVX_INLINE
|
||||
__m128 Scalar::eval() const noexcept
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
}
|
||||
36
src/tenvex/expressions/sub.h
Normal file
36
src/tenvex/expressions/sub.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "defines.h"
|
||||
#include "expression.h"
|
||||
#include "concepts.h"
|
||||
#include "traits.h"
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<vec_expr L, vec_expr R>
|
||||
struct Sub : Expr<Sub<L, R>>
|
||||
{
|
||||
TNVX_INLINE
|
||||
Sub(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
|
||||
|
||||
[[nodiscard]] TNVX_INLINE
|
||||
__m128 eval() const noexcept;
|
||||
|
||||
private:
|
||||
tnvx_ref_or_value_t<L> _l;
|
||||
tnvx_ref_or_value_t<R> _r;
|
||||
};
|
||||
|
||||
template<vec_expr L, vec_expr R>
|
||||
[[nodiscard]] TNVX_INLINE
|
||||
Sub<L, R> operator-(const L& l, const R& r) noexcept;
|
||||
|
||||
template<vec_expr L, vec_expr R>
|
||||
inline constexpr bool is_vec_expr<Sub<L, R>> = true;
|
||||
|
||||
}
|
||||
|
||||
#include "sub_impl.hpp"
|
||||
30
src/tenvex/expressions/sub_impl.hpp
Normal file
30
src/tenvex/expressions/sub_impl.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "sub.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<vec_expr L, vec_expr R>
|
||||
TNVX_INLINE
|
||||
Sub<L, R>::Sub(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
|
||||
: _l(l)
|
||||
, _r(r)
|
||||
{
|
||||
}
|
||||
|
||||
template<vec_expr L, vec_expr R>
|
||||
TNVX_INLINE
|
||||
__m128 Sub<L, R>::eval() const noexcept
|
||||
{
|
||||
return _mm_sub_ps(_l.eval(), _r.eval());
|
||||
}
|
||||
|
||||
template<vec_expr L, vec_expr R>
|
||||
TNVX_INLINE
|
||||
Sub<L, R> operator-(const L& l, const R& r) noexcept
|
||||
{
|
||||
return { l, r };
|
||||
}
|
||||
|
||||
}
|
||||
22
src/tenvex/expressions/traits.h
Normal file
22
src/tenvex/expressions/traits.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_stored_by_value_v = std::is_trivially_copyable_v<T>
|
||||
&& std::is_trivially_destructible_v<T>
|
||||
&& sizeof(T) <= 16;
|
||||
|
||||
template<typename T>
|
||||
using tnvx_ref_or_value_t = std::conditional_t<is_stored_by_value_v<T>, T, const T&>;
|
||||
|
||||
template<typename>
|
||||
inline constexpr bool is_scalar_expr = false;
|
||||
|
||||
template<typename>
|
||||
inline constexpr bool is_vec_expr = false;
|
||||
|
||||
}
|
||||
41
src/tenvex/expressions/vec4.h
Normal file
41
src/tenvex/expressions/vec4.h
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "defines.h"
|
||||
#include "expression.h"
|
||||
#include "traits.h"
|
||||
#include "concepts.h"
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
struct alignas(16) vec4 : Expr<vec4>
|
||||
{
|
||||
TNVX_INLINE
|
||||
vec4() noexcept = default;
|
||||
|
||||
TNVX_INLINE
|
||||
vec4(float x, float y, float z, float w = 0) noexcept;
|
||||
|
||||
template<vec_expr E>
|
||||
TNVX_INLINE
|
||||
vec4(const E& e) noexcept;
|
||||
|
||||
[[nodiscaed]] TNVX_INLINE
|
||||
__m128 eval() const noexcept;
|
||||
|
||||
private:
|
||||
__m128 _data;
|
||||
};
|
||||
|
||||
template<>
|
||||
inline constexpr bool is_vec_expr<vec4> = true;
|
||||
|
||||
TNVX_INLINE
|
||||
bool approx_eq(const vec4& lhs, const vec4& rhs, float eps = 1e-6f) noexcept;
|
||||
|
||||
TNVX_INLINE
|
||||
bool operator==(const vec4& lhs, const vec4& rhs) noexcept;
|
||||
|
||||
}
|
||||
1
src/tenvex/expressions/vec4_impl.hpp
Normal file
1
src/tenvex/expressions/vec4_impl.hpp
Normal file
@ -0,0 +1 @@
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "expression.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<expression L, expression R>
|
||||
struct Add : Expr<Add<L, R>>
|
||||
{
|
||||
inline
|
||||
Add(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
__m128 eval() const noexcept;
|
||||
|
||||
private:
|
||||
tnvx_type_storage<L> _l;
|
||||
tnvx_type_storage<R> _r;
|
||||
};
|
||||
|
||||
template<expression L, expression R>
|
||||
[[nodiscard]] inline
|
||||
Add<L, R> operator+(const Expr<L>& l, const Expr<R>& r) noexcept;
|
||||
|
||||
}
|
||||
|
||||
#include "add_impl.hpp"
|
||||
@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "add.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<expression L, expression R>
|
||||
Add<L, R>::Add(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
|
||||
: _l(l)
|
||||
, _r(r)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template<expression L, expression R>
|
||||
TNVX_INLINE
|
||||
__m128 Add<L, R>::eval() const noexcept
|
||||
{
|
||||
return _mm_add_ps(_l.eval(), _r.eval());
|
||||
}
|
||||
|
||||
template<expression L, expression R>
|
||||
TNVX_INLINE
|
||||
Add<L, R> operator+(const Expr<L>& l, const Expr<R>& r) noexcept
|
||||
{
|
||||
return { l.self(), r.self() };
|
||||
}
|
||||
|
||||
} // namespace tnvx
|
||||
@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "expression.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<vector_expression L, vector_expression R>
|
||||
struct Cross : Expr<Cross<L, R>>
|
||||
{
|
||||
inline
|
||||
Cross(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
__m128 eval() const noexcept;
|
||||
|
||||
private:
|
||||
tnvx_type_storage<L> _l;
|
||||
tnvx_type_storage<R> _r;
|
||||
};
|
||||
|
||||
template<vector_expression L, vector_expression R>
|
||||
[[nodiscard]] inline
|
||||
Cross<L, R> cross(const Expr<L>& l, const Expr<L>& r) noexcept;
|
||||
|
||||
}
|
||||
|
||||
#include "cross_impl.hpp"
|
||||
@ -1,47 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "cross.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<vector_expression L, vector_expression R>
|
||||
TNVX_INLINE
|
||||
Cross<L, R>::Cross(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
|
||||
: _l(l)
|
||||
, _r(r)
|
||||
{
|
||||
}
|
||||
|
||||
template<vector_expression L, vector_expression R>
|
||||
TNVX_INLINE
|
||||
__m128 Cross<L, R>::eval() const noexcept
|
||||
{
|
||||
__m128 a = _l.eval();
|
||||
__m128 b = _r.eval();
|
||||
|
||||
// [ay, az, ax, 0]
|
||||
__m128 a_yzx = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 0, 2, 1));
|
||||
// [by, bz, bx, 0]
|
||||
__m128 b_yzx = _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 0, 2, 1));
|
||||
|
||||
// [ay*bz, az*bx, ax*by, 0]
|
||||
__m128 mul0 = _mm_mul_ps(a, b_yzx);
|
||||
// [az*by, ax*bz, ay*bx, 0]
|
||||
__m128 mul1 = _mm_mul_ps(a_yzx, b);
|
||||
|
||||
// [ay*bz - az*by, az*bx - ax*bz, ax*by - ay*bx, 0]
|
||||
__m128 sub = _mm_sub_ps(mul0, mul1);
|
||||
|
||||
// shuffle обратно в [x, y, z, 0]
|
||||
return _mm_shuffle_ps(sub, sub, _MM_SHUFFLE(3, 0, 2, 1));
|
||||
}
|
||||
|
||||
template<vector_expression L, vector_expression R>
|
||||
Cross<L, R> cross(const Expr<L>& l, const Expr<R>& r) noexcept
|
||||
{
|
||||
return { l.self(), r.self() };
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "expression.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<vector_expression L, vector_expression R>
|
||||
struct Dot : Expr<Dot<L, R>>
|
||||
{
|
||||
inline
|
||||
Dot(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
__m128 eval() const noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
operator float() const noexcept;
|
||||
|
||||
private:
|
||||
tnvx_type_storage<L> _l;
|
||||
tnvx_type_storage<R> _r;
|
||||
};
|
||||
|
||||
template<vector_expression L, vector_expression R>
|
||||
[[nodiscard]] inline
|
||||
Dot<L, R> dot(const Expr<L>& l, const Expr<R>& r) noexcept;
|
||||
|
||||
template<vector_expression L, vector_expression R>
|
||||
[[nodiscard]] inline
|
||||
float operator*(const Dot<L, R>& l, float r) noexcept;
|
||||
|
||||
template<vector_expression L, vector_expression R>
|
||||
[[nodiscard]] inline
|
||||
float operator*(float l, const Dot<L, R>& r) noexcept;
|
||||
|
||||
}
|
||||
|
||||
#include "dot_impl.hpp"
|
||||
@ -1,60 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "dot.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<vector_expression L, vector_expression R>
|
||||
TNVX_INLINE
|
||||
Dot<L, R>::Dot(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
|
||||
: _l(l)
|
||||
, _r(r)
|
||||
{
|
||||
}
|
||||
|
||||
template<vector_expression L, vector_expression R>
|
||||
TNVX_INLINE
|
||||
__m128 Dot<L, R>::eval() const noexcept
|
||||
{
|
||||
__m128 a = _l.eval();
|
||||
__m128 b = _r.eval();
|
||||
|
||||
__m128 mul = _mm_mul_ps(a, b);
|
||||
__m128 shuf = _mm_shuffle_ps(mul, mul, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128 sum = _mm_add_ps(mul, shuf);
|
||||
shuf = _mm_movehl_ps(shuf, sum);
|
||||
sum = _mm_add_ss(sum, shuf);
|
||||
return _mm_shuffle_ps(sum, sum, 0);
|
||||
}
|
||||
|
||||
template<vector_expression L, vector_expression R>
|
||||
TNVX_INLINE
|
||||
Dot<L, R>::operator float() const noexcept
|
||||
{
|
||||
return _mm_cvtss_f32(eval());
|
||||
}
|
||||
|
||||
template<vector_expression L, vector_expression R>
|
||||
TNVX_INLINE
|
||||
Dot<L, R> dot(const Expr<L>& l, const Expr<R>& r) noexcept
|
||||
{
|
||||
return { l.self(), r.self() };
|
||||
}
|
||||
|
||||
template<vector_expression L, vector_expression R>
|
||||
TNVX_INLINE
|
||||
float operator*(const Dot<L, R>& l, float r) noexcept
|
||||
{
|
||||
return static_cast<float>(l) * r;
|
||||
}
|
||||
|
||||
template<vector_expression L, vector_expression R>
|
||||
float operator*(float l, const Dot<L, R>& r) noexcept
|
||||
{
|
||||
return l * static_cast<float>(r);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
#include <immintrin.h>
|
||||
#include <cstdint>
|
||||
#include <concepts>
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<typename Derived>
|
||||
struct Expr
|
||||
{
|
||||
[[nodiscard]] inline
|
||||
const Derived& self() const noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
__m128 eval() const noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
float operator[](size_t i) const noexcept;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
concept expression = requires(T a)
|
||||
{
|
||||
{ a.eval() } -> std::same_as<__m128>;
|
||||
}
|
||||
&& std::derived_from<T, Expr<T>>;
|
||||
|
||||
struct Scalar;
|
||||
|
||||
template<typename T>
|
||||
concept vector_expression = expression<T> && !std::same_as<T, Scalar>;
|
||||
|
||||
template<typename T>
|
||||
constexpr bool tnvx_store_by_value = std::is_trivially_copyable_v<T>
|
||||
&& std::is_trivially_destructible_v<T>
|
||||
&& sizeof(T) <= 16;
|
||||
|
||||
template<typename T>
|
||||
using tnvx_type_storage = std::conditional_t<tnvx_store_by_value<T>, T, const T&>;
|
||||
|
||||
} // namespace tnvx
|
||||
|
||||
#include "expression_impl.hpp"
|
||||
@ -1,31 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "expression.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<typename Derived>
|
||||
TNVX_INLINE
|
||||
const Derived& Expr<Derived>::self() const noexcept
|
||||
{
|
||||
return static_cast<const Derived&>(*this);
|
||||
}
|
||||
|
||||
template<typename Derived>
|
||||
TNVX_INLINE
|
||||
__m128 Expr<Derived>::eval() const noexcept
|
||||
{
|
||||
return self().eval();
|
||||
}
|
||||
|
||||
template<typename Derived>
|
||||
TNVX_INLINE
|
||||
float Expr<Derived>::operator[](size_t i) const noexcept
|
||||
{
|
||||
alignas(16) float tmp[4];
|
||||
_mm_store_ps(tmp, eval());
|
||||
return tmp[i];
|
||||
}
|
||||
|
||||
} // namespace tnvx
|
||||
@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "expression.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<vector_expression E>
|
||||
struct Magnitude : Expr<Magnitude<E>>
|
||||
{
|
||||
inline
|
||||
Magnitude(const E& TNVX_RESTRICT e) noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
__m128 eval() const noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
operator float() const noexcept;
|
||||
|
||||
private:
|
||||
tnvx_type_storage<E> _e;
|
||||
};
|
||||
|
||||
template<vector_expression E>
|
||||
[[nodiscard]] inline
|
||||
Magnitude<E> magnitude(const Expr<E>& e) noexcept;
|
||||
|
||||
}
|
||||
|
||||
#include "magnitude_impl.hpp"
|
||||
@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "magnitude.h"
|
||||
#include "dot.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<vector_expression E>
|
||||
TNVX_INLINE
|
||||
Magnitude<E>::Magnitude(const E& TNVX_RESTRICT e) noexcept
|
||||
: _e(e)
|
||||
{
|
||||
}
|
||||
|
||||
template<vector_expression E>
|
||||
TNVX_INLINE
|
||||
__m128 Magnitude<E>::eval() const noexcept
|
||||
{
|
||||
return _mm_sqrt_ss(Dot<E, E>(_e, _e).eval());
|
||||
}
|
||||
|
||||
template<vector_expression E>
|
||||
TNVX_INLINE
|
||||
Magnitude<E>::operator float() const noexcept
|
||||
{
|
||||
return _mm_cvtss_f32(eval());
|
||||
}
|
||||
|
||||
template<vector_expression E>
|
||||
TNVX_INLINE
|
||||
Magnitude<E> magnitude(const Expr<E>& e) noexcept
|
||||
{
|
||||
return { e.self() };
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "expression.h"
|
||||
#include "scalar.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<expression L, expression R>
|
||||
struct Mul : Expr<Mul<L, R>>
|
||||
{
|
||||
inline
|
||||
Mul(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
__m128 eval() const noexcept;
|
||||
|
||||
private:
|
||||
tnvx_type_storage<L> _l;
|
||||
tnvx_type_storage<R> _r;
|
||||
};
|
||||
|
||||
//template<vector_expression L, vector_expression R>
|
||||
//[[nodiscard]] inline
|
||||
//Mul<L, R> operator*(const Expr<L>& l, const Expr<R>& r) noexcept;
|
||||
|
||||
template<expression E>
|
||||
[[nodiscard]] inline
|
||||
Mul<E, Scalar> operator*(const Expr<E>& l, float r) noexcept;
|
||||
|
||||
template<expression E>
|
||||
[[nodiscard]] inline
|
||||
Mul<Scalar, E> operator*(float l, const Expr<E>& r) noexcept;
|
||||
|
||||
}
|
||||
|
||||
#include "mul_impl.hpp"
|
||||
@ -1,44 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "mul.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<expression L, expression R>
|
||||
TNVX_INLINE
|
||||
inline Mul<L, R>::Mul(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
|
||||
: _l(l)
|
||||
, _r(r)
|
||||
{
|
||||
}
|
||||
|
||||
template<expression L, expression R>
|
||||
TNVX_INLINE
|
||||
__m128 Mul<L, R>::eval() const noexcept
|
||||
{
|
||||
return _mm_mul_ps(_l.eval(), _r.eval());
|
||||
}
|
||||
|
||||
//template<vector_expression L, vector_expression R>
|
||||
//TNVX_INLINE
|
||||
//Mul<L, R> operator*(const Expr<L>& l, const Expr<R>& r) noexcept
|
||||
//{
|
||||
// return { l.self(), r.self() };
|
||||
//}
|
||||
|
||||
template<expression E>
|
||||
TNVX_INLINE
|
||||
Mul<E, Scalar> operator*(const Expr<E>& l, float r) noexcept
|
||||
{
|
||||
return { l.self(), Scalar(r) };
|
||||
}
|
||||
|
||||
template<expression E>
|
||||
TNVX_INLINE
|
||||
Mul<Scalar, E> operator*(float l, const Expr<E>& r) noexcept
|
||||
{
|
||||
return { Scalar(l), r.self() };
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "expression.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<vector_expression E>
|
||||
struct Norm : Expr<Norm<E>>
|
||||
{
|
||||
inline
|
||||
Norm(const E& TNVX_RESTRICT e) noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
__m128 eval() const noexcept;
|
||||
|
||||
private:
|
||||
tnvx_type_storage<E> _e;
|
||||
};
|
||||
|
||||
template<vector_expression E>
|
||||
[[nodiscard]] inline
|
||||
Norm<E> norm(const Expr<E>& e) noexcept;
|
||||
|
||||
}
|
||||
|
||||
#include "norm_impl.hpp"
|
||||
@ -1,38 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "norm.h"
|
||||
#include "dot.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<vector_expression E>
|
||||
TNVX_INLINE
|
||||
Norm<E>::Norm(const E& TNVX_RESTRICT e) noexcept
|
||||
: _e(e)
|
||||
{
|
||||
}
|
||||
|
||||
template<vector_expression E>
|
||||
TNVX_INLINE
|
||||
__m128 Norm<E>::eval() const noexcept
|
||||
{
|
||||
__m128 data = _e.eval();
|
||||
__m128 mul = _mm_mul_ps(data, data);
|
||||
__m128 shuf = _mm_shuffle_ps(mul, mul, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128 sum = _mm_add_ps(mul, shuf);
|
||||
shuf = _mm_movehl_ps(shuf, sum);
|
||||
sum = _mm_add_ss(sum, shuf);
|
||||
__m128 len = _mm_shuffle_ps(sum, sum, 0);
|
||||
__m128 sqrt_len = _mm_sqrt_ps(len);
|
||||
return _mm_div_ps(data, sqrt_len);
|
||||
}
|
||||
|
||||
template<vector_expression E>
|
||||
TNVX_INLINE
|
||||
Norm<E> norm(const Expr<E>& e) noexcept
|
||||
{
|
||||
return { e.self() };
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "expression.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
struct Scalar : Expr<Scalar>
|
||||
{
|
||||
inline
|
||||
Scalar(float value) noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
__m128 eval() const noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
float operator[](size_t) const noexcept;
|
||||
|
||||
private:
|
||||
float _value;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "scalar_impl.hpp"
|
||||
@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "scalar.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
TNVX_INLINE
|
||||
Scalar::Scalar(float value) noexcept
|
||||
: _value(value)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] TNVX_INLINE
|
||||
__m128 Scalar::eval() const noexcept
|
||||
{
|
||||
return _mm_set1_ps(_value);
|
||||
}
|
||||
|
||||
[[nodiscard]] TNVX_INLINE
|
||||
float Scalar::operator[](size_t) const noexcept
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "expression.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<expression L, expression R>
|
||||
struct Sub : Expr<Sub<L, R>>
|
||||
{
|
||||
inline
|
||||
Sub(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
__m128 eval() const noexcept;
|
||||
|
||||
private:
|
||||
tnvx_type_storage<L> _l;
|
||||
tnvx_type_storage<R> _r;
|
||||
};
|
||||
|
||||
template<expression L, expression R>
|
||||
[[nodiscard]] inline
|
||||
Sub<L, R> operator-(const Expr<L>& l, const Expr<R>& r) noexcept;
|
||||
|
||||
}
|
||||
|
||||
#include "sub_impl.hpp"
|
||||
@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "sub.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<expression L, expression R>
|
||||
tnvx::Sub<L, R>::Sub(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept
|
||||
: _l(l), _r(r)
|
||||
{
|
||||
}
|
||||
|
||||
template<expression L, expression R>
|
||||
TNVX_INLINE
|
||||
__m128 Sub<L, R>::eval() const noexcept
|
||||
{
|
||||
return _mm_sub_ps(_l.eval(), _r.eval());
|
||||
}
|
||||
|
||||
template<expression L, expression R>
|
||||
TNVX_INLINE
|
||||
Sub<L, R> operator-(const Expr<L>& l, const Expr<R>& r) noexcept
|
||||
{
|
||||
return { l.self(), r.self() };
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,12 +1,11 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "vec4.h"
|
||||
|
||||
#include "internal/add.h"
|
||||
#include "internal/sub.h"
|
||||
#include "internal/mul.h"
|
||||
#include "internal/dot.h"
|
||||
#include "internal/cross.h"
|
||||
#include "internal/norm.h"
|
||||
#include "internal/magnitude.h"
|
||||
#include "internal/scalar.h"
|
||||
#include "expressions/defines.h"
|
||||
#include "expressions/traits.h"
|
||||
#include "expressions/expression.h"
|
||||
#include "expressions/concepts.h"
|
||||
#include "expressions/add.h"
|
||||
#include "expressions/sub.h"
|
||||
#include "expressions/scalar.h"
|
||||
#include "expressions/mul.h"
|
||||
#include "expressions/vec4.h"
|
||||
|
||||
@ -1,63 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "internal/expression.h"
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
struct alignas(16) vec4 : Expr<vec4>
|
||||
{
|
||||
vec4() = default;
|
||||
|
||||
inline
|
||||
vec4(float x, float y, float z, float w) noexcept;
|
||||
|
||||
template<typename E>
|
||||
inline
|
||||
vec4(const Expr<E>& expr) noexcept;
|
||||
|
||||
template<typename E>
|
||||
inline
|
||||
vec4& operator=(const Expr<E>& expr) noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
__m128 eval() const noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
auto magnitude() const noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
auto norm() const noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
float x() const noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
float y() const noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
float z() const noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
float w() const noexcept;
|
||||
|
||||
[[nodiscard]] inline
|
||||
std::array<float, 4> data() const noexcept;
|
||||
|
||||
private:
|
||||
__m128 _data;
|
||||
};
|
||||
|
||||
inline
|
||||
bool operator==(const vec4& a, const vec4& b) noexcept;
|
||||
|
||||
inline
|
||||
bool approx_eq(const vec4& a, const vec4& b, float eps = 1e-3f) noexcept;
|
||||
|
||||
}
|
||||
|
||||
#include "vec4_impl.hpp"
|
||||
@ -1,108 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "vec4.h"
|
||||
#include "internal/magnitude.h"
|
||||
#include "internal/norm.h"
|
||||
|
||||
namespace tnvx
|
||||
{
|
||||
|
||||
template<typename E>
|
||||
TNVX_INLINE
|
||||
vec4::vec4(const Expr<E>& expr) noexcept
|
||||
: _data(expr.eval())
|
||||
{
|
||||
}
|
||||
|
||||
template<typename E>
|
||||
TNVX_INLINE
|
||||
vec4& vec4::operator=(const Expr<E>& expr) noexcept
|
||||
{
|
||||
_data = expr.eval();
|
||||
return *this;
|
||||
}
|
||||
|
||||
TNVX_INLINE
|
||||
vec4::vec4(float x, float y, float z, float w) noexcept
|
||||
{
|
||||
_data = _mm_set_ps(w, z, y, x);
|
||||
}
|
||||
|
||||
TNVX_INLINE
|
||||
__m128 vec4::eval() const noexcept
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
TNVX_INLINE
|
||||
auto vec4::magnitude() const noexcept
|
||||
{
|
||||
return Magnitude<vec4>(*this);
|
||||
}
|
||||
|
||||
TNVX_INLINE
|
||||
auto vec4::norm() const noexcept
|
||||
{
|
||||
return Norm<vec4>(*this);
|
||||
}
|
||||
|
||||
//TNVX_INLINE
|
||||
//Magnitude<vec4> vec4::magnitude() const noexcept
|
||||
//{
|
||||
// return { *this };
|
||||
//}
|
||||
//
|
||||
//TNVX_INLINE
|
||||
//Norm<vec4> vec4::norm() const noexcept
|
||||
//{
|
||||
// return { *this };
|
||||
//}
|
||||
|
||||
TNVX_INLINE
|
||||
float vec4::x() const noexcept
|
||||
{
|
||||
return _mm_cvtss_f32(_data);
|
||||
}
|
||||
|
||||
TNVX_INLINE
|
||||
float vec4::y() const noexcept
|
||||
{
|
||||
return _mm_cvtss_f32(_mm_shuffle_ps(_data, _data, _MM_SHUFFLE(1, 1, 1, 1)));
|
||||
}
|
||||
|
||||
TNVX_INLINE
|
||||
float vec4::z() const noexcept
|
||||
{
|
||||
return _mm_cvtss_f32(_mm_shuffle_ps(_data, _data, _MM_SHUFFLE(2, 2, 2, 2)));
|
||||
}
|
||||
|
||||
TNVX_INLINE
|
||||
float vec4::w() const noexcept
|
||||
{
|
||||
return _mm_cvtss_f32(_mm_shuffle_ps(_data, _data, _MM_SHUFFLE(3, 3, 3, 3)));
|
||||
}
|
||||
|
||||
TNVX_INLINE
|
||||
std::array<float, 4> vec4::data() const noexcept
|
||||
{
|
||||
std::array<float, 4> result;
|
||||
_mm_storeu_ps(result.data(), _data);
|
||||
return result;
|
||||
}
|
||||
|
||||
TNVX_INLINE
|
||||
bool operator==(const vec4& a, const vec4& b) noexcept
|
||||
{
|
||||
return _mm_movemask_ps(_mm_cmpeq_ps(a.eval(), b.eval())) == 0xF;
|
||||
}
|
||||
|
||||
TNVX_INLINE
|
||||
bool approx_eq(const vec4& a, const vec4& b, float eps) noexcept
|
||||
{
|
||||
__m128 epsilon = _mm_set1_ps(eps);
|
||||
__m128 diff = _mm_sub_ps(a.eval(), b.eval());
|
||||
__m128 abs_diff = _mm_andnot_ps(_mm_set1_ps(-0.0f), diff);
|
||||
return _mm_movemask_ps(_mm_cmplt_ps(abs_diff, epsilon)) == 0xF;
|
||||
}
|
||||
|
||||
}
|
||||
@ -9,7 +9,7 @@ TEST(vec4, add)
|
||||
{
|
||||
vec4 a = { 1.0f, 2.0f, 3.0f, 4.0f };
|
||||
vec4 b = { 4.0f, 3.0f, 2.0f, 1.0f };
|
||||
vec4 check{ 5.0f, 5.0f, 5.0f, 5.0f };
|
||||
vec4 check = { 5.0f, 5.0f, 5.0f, 5.0f };
|
||||
vec4 result = a + b;
|
||||
|
||||
EXPECT_TRUE(approx_eq(check, result));
|
||||
@ -140,6 +140,19 @@ TEST(vec4, expression_c)
|
||||
EXPECT_TRUE(approx_eq(check, result));
|
||||
}
|
||||
|
||||
TEST(vec4, expression_d)
|
||||
{
|
||||
vec4 a = { 1.0f, 2.0f, 3.0f, 0.0f };
|
||||
vec4 b = { 4.0f, 5.0f, 6.0f, 0.0f };
|
||||
vec4 c = { 1.0f, 0.0f, 1.0f, 0.0f };
|
||||
|
||||
vec4 check = { 7.243f, 5.657f, 10.071f, 0.0f };
|
||||
|
||||
vec4 result = magnitude(a) * dot(b, c) + c * 3.0f;
|
||||
|
||||
EXPECT_TRUE(approx_eq(check, result));
|
||||
}
|
||||
|
||||
TEST(vec4, cross_anticommutative)
|
||||
{
|
||||
vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f };
|
||||
|
||||
Loading…
Reference in New Issue
Block a user