Compare commits
No commits in common. "5367266aa67257d309bf0811b267d1fbf78118af" and "d3a26c598960771a545468e4a9efad79f0cd1943" have entirely different histories.
5367266aa6
...
d3a26c5989
@ -158,7 +158,3 @@ project "tenvex_tests"
|
|||||||
|
|
||||||
filter "system:macosx"
|
filter "system:macosx"
|
||||||
pic "on"
|
pic "on"
|
||||||
|
|
||||||
filter "toolset:gcc or toolset:clang"
|
|
||||||
buildoptions { "-msse4.1" }
|
|
||||||
filter {}
|
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define HAS_SSE41 1
|
#define HAS_SSE42 1
|
||||||
|
|||||||
@ -3,11 +3,10 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
|
|
||||||
#if defined(HAS_SSE41)
|
#if defined(HAS_SSE42)
|
||||||
#include <smmintrin.h>
|
#include <smmintrin.h>
|
||||||
using vf4 = __m128;
|
using vf4 = __m128;
|
||||||
#else
|
#else
|
||||||
#error "tenvex requires an SSE4.1 backend (define HAS_SSE42 and build with -msse4.1 on GCC/Clang)"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace tnvx::detail
|
namespace tnvx::detail
|
||||||
@ -32,13 +31,6 @@ vf4 scalar(float value) noexcept
|
|||||||
return _mm_set_ps1(value);
|
return _mm_set_ps1(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<int lane>
|
|
||||||
[[nodiscard]] TNVX_INLINE
|
|
||||||
float get_lane(vf4 v) noexcept
|
|
||||||
{
|
|
||||||
return _mm_cvtss_f32(_mm_shuffle_ps(v, v, _MM_SHUFFLE(0, 0, 0, lane)));
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] TNVX_INLINE
|
[[nodiscard]] TNVX_INLINE
|
||||||
vf4 add(vf4 l, vf4 r) noexcept
|
vf4 add(vf4 l, vf4 r) noexcept
|
||||||
{
|
{
|
||||||
|
|||||||
@ -24,11 +24,6 @@ private:
|
|||||||
tnvx_ref_or_value_t<R> _r;
|
tnvx_ref_or_value_t<R> _r;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// this banch of reloads (instead of expression*expression) is required for two thigs:
|
|
||||||
// 1. to avoid ambiguity with operator* for vector-vector multiplication
|
|
||||||
// 2. to avoid scalar operator collapses to float instead of lazy-expressions
|
|
||||||
|
|
||||||
template<vec_expr E>
|
template<vec_expr E>
|
||||||
[[nodiscard]] TNVX_INLINE
|
[[nodiscard]] TNVX_INLINE
|
||||||
Mul<E, Scalar> operator*(const E& l, float r) noexcept;
|
Mul<E, Scalar> operator*(const E& l, float r) noexcept;
|
||||||
@ -41,22 +36,6 @@ template<scalar_expr L, scalar_expr R>
|
|||||||
[[nodiscard]] TNVX_INLINE
|
[[nodiscard]] TNVX_INLINE
|
||||||
Mul<L, R> operator*(const L& l, const R& r) noexcept;
|
Mul<L, R> operator*(const L& l, const R& r) noexcept;
|
||||||
|
|
||||||
template<scalar_expr L, vec_expr R>
|
|
||||||
[[nodiscard]] TNVX_INLINE
|
|
||||||
Mul<L, R> operator*(const L& l, const R& r) noexcept;
|
|
||||||
|
|
||||||
template<vec_expr L, scalar_expr R>
|
|
||||||
[[nodiscard]] TNVX_INLINE
|
|
||||||
Mul<L, R> operator*(const L& l, const R& r) noexcept;
|
|
||||||
|
|
||||||
template<scalar_expr E>
|
|
||||||
[[nodiscard]] TNVX_INLINE
|
|
||||||
Mul<E, Scalar> operator*(const E& l, float r) noexcept;
|
|
||||||
|
|
||||||
template<scalar_expr E>
|
|
||||||
[[nodiscard]] TNVX_INLINE
|
|
||||||
Mul<Scalar, E> operator*(float l, const E& r) noexcept;
|
|
||||||
|
|
||||||
template<vec_expr L, scalar_expr R> inline constexpr bool is_vec_expr<Mul<L, R>> = true;
|
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, vec_expr R> inline constexpr bool is_vec_expr<Mul<L, R>> = true;
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ template<expression L, expression R>
|
|||||||
TNVX_INLINE
|
TNVX_INLINE
|
||||||
vf4 Mul<L, R>::eval() const noexcept
|
vf4 Mul<L, R>::eval() const noexcept
|
||||||
{
|
{
|
||||||
return detail::mul(_l.eval(), _r.eval());
|
return _mm_mul_ps(_l.eval(), _r.eval());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<vec_expr E>
|
template<vec_expr E>
|
||||||
@ -41,32 +41,4 @@ Mul<L, R> operator*(const L& l, const R& r) noexcept
|
|||||||
return { l, r };
|
return { l, r };
|
||||||
}
|
}
|
||||||
|
|
||||||
template<scalar_expr L, vec_expr R>
|
|
||||||
TNVX_INLINE
|
|
||||||
Mul<L, R> operator*(const L& l, const R& r) noexcept
|
|
||||||
{
|
|
||||||
return { l, r };
|
|
||||||
}
|
|
||||||
|
|
||||||
template<vec_expr L, scalar_expr R>
|
|
||||||
TNVX_INLINE
|
|
||||||
Mul<L, R> operator*(const L& l, const R& r) noexcept
|
|
||||||
{
|
|
||||||
return { l, r };
|
|
||||||
}
|
|
||||||
|
|
||||||
template<scalar_expr E>
|
|
||||||
TNVX_INLINE
|
|
||||||
Mul<E, Scalar> operator*(const E& l, float r) noexcept
|
|
||||||
{
|
|
||||||
return { l, Scalar(r) };
|
|
||||||
}
|
|
||||||
|
|
||||||
template<scalar_expr E>
|
|
||||||
TNVX_INLINE
|
|
||||||
Mul<Scalar, E> operator*(float l, const E& r) noexcept
|
|
||||||
{
|
|
||||||
return { Scalar(l), r };
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,23 +20,10 @@ struct alignas(16) vec4 : Expr<vec4>
|
|||||||
TNVX_INLINE
|
TNVX_INLINE
|
||||||
vec4(const E& e) noexcept;
|
vec4(const E& e) noexcept;
|
||||||
|
|
||||||
[[nodiscard]] TNVX_INLINE
|
|
||||||
float x() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] TNVX_INLINE
|
|
||||||
float y() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] TNVX_INLINE
|
|
||||||
float z() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] TNVX_INLINE
|
|
||||||
float w() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] TNVX_INLINE
|
[[nodiscard]] TNVX_INLINE
|
||||||
vf4 eval() const noexcept;
|
vf4 eval() const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
vf4 _data;
|
vf4 _data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -31,33 +31,9 @@ bool approx_eq(const vec4& lhs, const vec4& rhs, float eps) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
TNVX_INLINE
|
TNVX_INLINE
|
||||||
bool operator==(const vec4& lhs, const vec4& rhs) noexcept
|
bool tnvx::operator==(const vec4& lhs, const vec4& rhs) noexcept
|
||||||
{
|
{
|
||||||
return detail::eq(lhs.eval(), rhs.eval());
|
return detail::eq(lhs.eval(), rhs.eval());
|
||||||
}
|
}
|
||||||
|
|
||||||
TNVX_INLINE
|
|
||||||
float vec4::x() const noexcept
|
|
||||||
{
|
|
||||||
return detail::get_lane<0>(_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
TNVX_INLINE
|
|
||||||
float vec4::y() const noexcept
|
|
||||||
{
|
|
||||||
return detail::get_lane<1>(_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
TNVX_INLINE
|
|
||||||
float vec4::z() const noexcept
|
|
||||||
{
|
|
||||||
return detail::get_lane<2>(_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
TNVX_INLINE
|
|
||||||
float vec4::w() const noexcept
|
|
||||||
{
|
|
||||||
return detail::get_lane<3>(_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,17 +15,6 @@ TEST(vec4, add)
|
|||||||
EXPECT_TRUE(approx_eq(check, result));
|
EXPECT_TRUE(approx_eq(check, result));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(vec4, mixed_type_add)
|
|
||||||
{
|
|
||||||
vec4 a = { 1.0f, 2.0f, 3.0f, 0.0f };
|
|
||||||
vec4 b = { 1.0f, 1.0f, 1.0f, 0.0f };
|
|
||||||
vec4 c = { 2.0f, 0.0f, 0.0f, 0.0f };
|
|
||||||
vec4 check = { 4.0f, 3.0f, 4.0f, 0.0f };
|
|
||||||
|
|
||||||
vec4 result = a + b + c;
|
|
||||||
EXPECT_TRUE(approx_eq(check, result));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(vec4, sub)
|
TEST(vec4, sub)
|
||||||
{
|
{
|
||||||
vec4 a = { 5.0f, 5.0f, 5.0f, 5.0f };
|
vec4 a = { 5.0f, 5.0f, 5.0f, 5.0f };
|
||||||
@ -163,81 +152,30 @@ TEST(vec4, expression_c)
|
|||||||
EXPECT_TRUE(approx_eq(check, result));
|
EXPECT_TRUE(approx_eq(check, result));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(vec4, expression_d)
|
|
||||||
{
|
|
||||||
vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f };
|
|
||||||
vec4 b = { 0.0f, 2.0f, 0.0f, 0.0f };
|
|
||||||
vec4 c = { 0.0f, 0.0f, 3.0f, 0.0f };
|
|
||||||
vec4 result = (a + b) * 2.0f + c - a;
|
|
||||||
vec4 check = { 1.0f, 4.0f, 3.0f, 0.0f };
|
|
||||||
EXPECT_TRUE(approx_eq(check, result));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(vec4, expression_from_temporaries)
|
|
||||||
{
|
|
||||||
vec4 a = { 1.0f, 2.0f, 3.0f, 0.0f };
|
|
||||||
vec4 b = { 4.0f, 5.0f, 6.0f, 0.0f };
|
|
||||||
vec4 check = { 2.0f, 4.0f, 6.0f, 0.0f };
|
|
||||||
vec4 result = (a + b) + (a - b); // intermediate ties are temporary
|
|
||||||
|
|
||||||
EXPECT_TRUE(approx_eq(check, result));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(vec4, cross_anticommutative)
|
TEST(vec4, cross_anticommutative)
|
||||||
{
|
{
|
||||||
vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f };
|
vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f };
|
||||||
vec4 b = { 0.0f, 1.0f, 0.0f, 0.0f };
|
vec4 b = { 0.0f, 1.0f, 0.0f, 0.0f };
|
||||||
|
|
||||||
EXPECT_TRUE(approx_eq(cross3(a, b), cross3(b, a) * -1.0f));
|
EXPECT_TRUE(approx_eq(cross3(a, b), cross3(b, a) * -1.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(vec4, w_is_zero_with_nonzero_input_w)
|
|
||||||
{
|
|
||||||
vec4 a = { 1.0f, 0.0f, 0.0f, 5.0f };
|
|
||||||
vec4 b = { 0.0f, 1.0f, 0.0f, 7.0f };
|
|
||||||
|
|
||||||
vec4 r = cross3(a, b);
|
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(r.w(), 0.0f);
|
|
||||||
EXPECT_FLOAT_EQ(r.z(), 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(vec4, preserves_nontrivial_w)
|
|
||||||
{
|
|
||||||
vec4 a = { 0.0f, 3.0f, 0.0f, 9.0f };
|
|
||||||
vec4 r = norm3(a);
|
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(r.y(), 1.0f);
|
|
||||||
EXPECT_FLOAT_EQ(r.w(), 9.0f);
|
|
||||||
}
|
|
||||||
TEST(vec4, dot_commutative)
|
TEST(vec4, dot_commutative)
|
||||||
{
|
{
|
||||||
vec4 a = { 1.0f, 2.0f, 3.0f, 0.0f };
|
vec4 a = { 1.0f, 2.0f, 3.0f, 0.0f };
|
||||||
vec4 b = { 4.0f, 5.0f, 6.0f, 0.0f };
|
vec4 b = { 4.0f, 5.0f, 6.0f, 0.0f };
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(dot3(a, b), dot3(b, a));
|
EXPECT_FLOAT_EQ(dot3(a, b), dot3(b, a));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(vec4, norm_magnitude)
|
TEST(vec4, norm_magnitude)
|
||||||
{
|
{
|
||||||
vec4 a = { 3.0f, 1.0f, 4.0f, 0.0f };
|
vec4 a = { 3.0f, 1.0f, 4.0f, 0.0f };
|
||||||
|
|
||||||
EXPECT_NEAR(magnitude3(norm3(a)), 1.0f, 1e-6f);
|
EXPECT_NEAR(magnitude3(norm3(a)), 1.0f, 1e-6f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(vec4, zero_vector_is_nan)
|
|
||||||
{
|
|
||||||
vec4 z = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|
||||||
vec4 r = norm3(z);
|
|
||||||
|
|
||||||
EXPECT_TRUE(std::isnan(r.x()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(vec4, dot_perpendicular)
|
TEST(vec4, dot_perpendicular)
|
||||||
{
|
{
|
||||||
vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f };
|
vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f };
|
||||||
vec4 b = { 0.0f, 1.0f, 0.0f, 0.0f };
|
vec4 b = { 0.0f, 1.0f, 0.0f, 0.0f };
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(dot3(a, b), 0.0f);
|
EXPECT_FLOAT_EQ(dot3(a, b), 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,29 +184,7 @@ TEST(vec4, cross_parallel)
|
|||||||
vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f };
|
vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f };
|
||||||
vec4 b = { 2.0f, 0.0f, 0.0f, 0.0f };
|
vec4 b = { 2.0f, 0.0f, 0.0f, 0.0f };
|
||||||
vec4 check = { 0.0f, 0.0f, 0.0f, 0.0f };
|
vec4 check = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||||
|
|
||||||
EXPECT_TRUE(approx_eq(cross3(a, b), check));
|
EXPECT_TRUE(approx_eq(cross3(a, b), check));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(vec4, dot3_times_literal_collapses)
|
|
||||||
{
|
|
||||||
vec4 a = { 1.0f, 2.0f, 3.0f, 0.0f };
|
|
||||||
vec4 b = { 4.0f, 5.0f, 6.0f, 0.0f };
|
|
||||||
|
|
||||||
auto r = dot3(a, b) * 2.0f;
|
|
||||||
|
|
||||||
static_assert(!std::is_same_v<decltype(r), float>);
|
|
||||||
static_assert(scalar_expr<decltype(r)>);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(vec4, magnitude3_times_literal_collapses)
|
|
||||||
{
|
|
||||||
vec4 a = { 3.0f, 0.0f, 4.0f, 0.0f };
|
|
||||||
|
|
||||||
auto r = magnitude3(a) * 2.0f;
|
|
||||||
|
|
||||||
static_assert(!std::is_same_v<decltype(r), float>);
|
|
||||||
static_assert(scalar_expr<decltype(r)>);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user