diff --git a/src/tenvex/internal/add.h b/src/tenvex/internal/add.h new file mode 100644 index 0000000..f6d52fa --- /dev/null +++ b/src/tenvex/internal/add.h @@ -0,0 +1,28 @@ +#pragma once + +#include "expression.h" + +namespace tnvx +{ + +template +struct Add : Expr> +{ + inline + Add(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept; + + [[nodiscard]] inline + __m128 eval() const noexcept; + +private: + tnvx_type_storage _l; + tnvx_type_storage _r; +}; + +template +[[nodiscard]] inline +Add operator+(const Expr& l, const Expr& r) noexcept; + +} + +#include "add_impl.hpp" diff --git a/src/tenvex/internal/add_impl.hpp b/src/tenvex/internal/add_impl.hpp new file mode 100644 index 0000000..0267546 --- /dev/null +++ b/src/tenvex/internal/add_impl.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include "add.h" + +namespace tnvx +{ + +template +Add::Add(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept + : _l(l) + , _r(r) +{ + +} + +template +TNVX_INLINE +__m128 Add::eval() const noexcept +{ + return _mm_add_ps(_l.eval(), _r.eval()); + +} + +template +TNVX_INLINE +Add operator+(const Expr& l, const Expr& r) noexcept +{ + return { l.self(), r.self() }; +} + +} // namespace tnvx diff --git a/src/tenvex/internal/cross.h b/src/tenvex/internal/cross.h new file mode 100644 index 0000000..c7b3e94 --- /dev/null +++ b/src/tenvex/internal/cross.h @@ -0,0 +1,28 @@ +#pragma once + +#include "expression.h" + +namespace tnvx +{ + +template +struct Cross : Expr> +{ + inline + Cross(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept; + + [[nodiscard]] inline + __m128 eval() const noexcept; + +private: + tnvx_type_storage _l; + tnvx_type_storage _r; +}; + +template +[[nodiscard]] inline +Cross cross(const Expr& l, const Expr& r) noexcept; + +} + +#include "cross_impl.hpp" diff --git a/src/tenvex/internal/cross_impl.hpp b/src/tenvex/internal/cross_impl.hpp new file mode 100644 index 0000000..f0a45c6 --- /dev/null +++ b/src/tenvex/internal/cross_impl.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include "cross.h" + +namespace tnvx +{ + +template +TNVX_INLINE +Cross::Cross(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept + : _l(l) + , _r(r) +{ +} + +template +TNVX_INLINE +__m128 Cross::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 +Cross cross(const Expr& l, const Expr& r) noexcept +{ + return { l.self(), r.self() }; +} + + +} diff --git a/src/tenvex/internal/defines.h b/src/tenvex/internal/defines.h new file mode 100644 index 0000000..28f1b98 --- /dev/null +++ b/src/tenvex/internal/defines.h @@ -0,0 +1,12 @@ +#pragma once + +#if defined(_MSC_VER) +#define TNVX_INLINE __forceinline +#define TNVX_RESTRICT __restrict +#elif defined(__GNUC__) || defined(__clang__) +#define TNVX_INLINE __attribute__((always_inline)) inline +#define TNVX_RESTRICT __restrict__ +#else +#define TNVX_INLINE inline +#define TNVX_RESTRICT +#endif diff --git a/src/tenvex/internal/dot.h b/src/tenvex/internal/dot.h new file mode 100644 index 0000000..974067b --- /dev/null +++ b/src/tenvex/internal/dot.h @@ -0,0 +1,31 @@ +#pragma once + +#include "expression.h" +#include "scalar.h" +#include "../vec4.h" + +namespace tnvx +{ + +template +requires (!std::same_as && !std::same_as) +struct Dot : Expr +{ + inline + Dot(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept; + + [[nodiscard]] inline + __m128 eval() const noexcept; + +private: + tnvx_type_storage _l; + tnvx_type_storage _r; +}; + +template +[[nodiscard]] inline +Dot dot(const Expr& l, const Expr& r) noexcept; + +} + +#include "dot_impl.hpp" diff --git a/src/tenvex/internal/dot_impl.hpp b/src/tenvex/internal/dot_impl.hpp new file mode 100644 index 0000000..ebbd27f --- /dev/null +++ b/src/tenvex/internal/dot_impl.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "dot.h" + +namespace tnvx +{ + +template +TNVX_INLINE +Dot::Dot(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept + : _l(l) + , _r(r) +{ +} + +template +TNVX_INLINE +__m128 Dot::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 +Dot dot(const Expr& l, const Expr& r) noexcept +{ + return { l.self(), r.self() }; +} + +} diff --git a/src/tenvex/internal/expression.h b/src/tenvex/internal/expression.h new file mode 100644 index 0000000..14cccd5 --- /dev/null +++ b/src/tenvex/internal/expression.h @@ -0,0 +1,47 @@ +#pragma once + +#include "defines.h" + +#include +#include +#include + +namespace tnvx +{ + +template +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 +concept expression = requires(T a) +{ + { a.eval() } -> std::same_as<__m128>; +} +&& std::derived_from>; + +class Scalar; + +template +concept vector_expression = expression && !std::same_as; + +template +constexpr bool tnvx_store_by_value = std::is_trivially_copyable_v + && std::is_trivially_destructible_v + && sizeof(T) <= 16; + +template +using tnvx_type_storage = std::conditional_t, T, const T&>; + +} // namespace tnvx + +#include "expression_impl.hpp" diff --git a/src/tenvex/internal/expression_impl.hpp b/src/tenvex/internal/expression_impl.hpp new file mode 100644 index 0000000..c52d41a --- /dev/null +++ b/src/tenvex/internal/expression_impl.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include "expression.h" + +namespace tnvx +{ + +template +TNVX_INLINE +const Derived& Expr::self() const noexcept +{ + return static_cast(*this); +} + +template +TNVX_INLINE +__m128 Expr::eval() const noexcept +{ + return self().eval(); +} + +template +TNVX_INLINE +float Expr::operator[](size_t i) const noexcept +{ + alignas(16) float tmp[4]; + _mm_store_ps(tmp, eval()); + return tmp[i]; +} + +} // namespace tnvx \ No newline at end of file diff --git a/src/tenvex/internal/magnitude.h b/src/tenvex/internal/magnitude.h new file mode 100644 index 0000000..8a30ffa --- /dev/null +++ b/src/tenvex/internal/magnitude.h @@ -0,0 +1,27 @@ +#pragma once + +#include "expression.h" + +namespace tnvx +{ + +template +struct Magnitude : Expr> +{ + inline + Magnitude(const E& TNVX_RESTRICT e) noexcept; + + [[nodiscard]] inline + __m128 eval() const noexcept; + +private: + tnvx_type_storage _e; +}; + +template +[[nodiscard]] inline +Magnitude magnitude(const Expr e) noexcept; + +} + +#include "magnitude_impl.hpp" diff --git a/src/tenvex/internal/magnitude_impl.hpp b/src/tenvex/internal/magnitude_impl.hpp new file mode 100644 index 0000000..5d9db46 --- /dev/null +++ b/src/tenvex/internal/magnitude_impl.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "magnitude.h" +#include "dot.h" + +namespace tnvx +{ + +template +TNVX_INLINE +Magnitude::Magnitude(const E& TNVX_RESTRICT e) noexcept + : _e(e) +{ +} + +template +TNVX_INLINE +__m128 Magnitude::eval() const noexcept +{ + return _mm_sqrt_ps(Dot(_e, _e).eval()); +} + +template +TNVX_INLINE +Magnitude magnitude(const Expr e) noexcept +{ + return { e.self() }; +} + +} diff --git a/src/tenvex/internal/mul.h b/src/tenvex/internal/mul.h new file mode 100644 index 0000000..50b35ff --- /dev/null +++ b/src/tenvex/internal/mul.h @@ -0,0 +1,33 @@ +#pragma once + +#include "expression.h" +#include "scalar.h" + +namespace tnvx +{ + +template +struct Mul : Expr> +{ + inline + Mul(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept; + + [[nodiscard]] inline + __m128 eval() const noexcept; + +private: + tnvx_type_storage _l; + tnvx_type_storage _r; +}; + +template +[[nodiscard]] inline +Mul operator*(const Expr& l, float r) noexcept; + +template +[[nodiscard]] inline +Mul operator*(float l, const Expr& r) noexcept; + +} + +#include "mul_impl.hpp" diff --git a/src/tenvex/internal/mul_impl.hpp b/src/tenvex/internal/mul_impl.hpp new file mode 100644 index 0000000..60f31a9 --- /dev/null +++ b/src/tenvex/internal/mul_impl.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "mul.h" + +namespace tnvx +{ + +template +TNVX_INLINE +inline Mul::Mul(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept + : _l(l) + , _r(r) +{ +} + +template +TNVX_INLINE +__m128 Mul::eval() const noexcept +{ + return _mm_mul_ps(_l.eval(), _r.eval()); +} + +template +TNVX_INLINE +Mul operator*(const Expr& l, float r) noexcept +{ + return { l.self(), Scalar(r) }; +} + +template +TNVX_INLINE +Mul operator*(float l, const Expr& r) noexcept +{ + return { Scalar(l), r.self() }; +} + +} diff --git a/src/tenvex/internal/norm.h b/src/tenvex/internal/norm.h new file mode 100644 index 0000000..8882029 --- /dev/null +++ b/src/tenvex/internal/norm.h @@ -0,0 +1,27 @@ +#pragma once + +#include "expression.h" + +namespace tnvx +{ + +template +struct Norm : Expr> +{ + inline + Norm(const E& TNVX_RESTRICT e) noexcept; + + [[nodiscard]] inline + __m128 eval() const noexcept; + +private: + tnvx_type_storage _e; +}; + +template +[[nodiscard]] inline +Norm norm(const Expr e) noexcept; + +} + +#include "norm_impl.hpp" diff --git a/src/tenvex/internal/norm_impl.hpp b/src/tenvex/internal/norm_impl.hpp new file mode 100644 index 0000000..215e9bf --- /dev/null +++ b/src/tenvex/internal/norm_impl.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "norm.h" +#include "dot.h" + +namespace tnvx +{ + +template +TNVX_INLINE +Norm::Norm(const E& TNVX_RESTRICT e) noexcept + : _e(e) +{ +} + +template +TNVX_INLINE +__m128 Norm::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); + return _mm_div_ps(data, len); +} + +template +Norm norm(const Expr e) noexcept +{ + return { e.self() }; +} + +} diff --git a/src/tenvex/internal/scalar.h b/src/tenvex/internal/scalar.h new file mode 100644 index 0000000..2fc298c --- /dev/null +++ b/src/tenvex/internal/scalar.h @@ -0,0 +1,25 @@ +#pragma once + +#include "expression.h" + +namespace tnvx +{ + +struct Scalar : Expr +{ + 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" diff --git a/src/tenvex/internal/scalar_impl.hpp b/src/tenvex/internal/scalar_impl.hpp new file mode 100644 index 0000000..f4804c7 --- /dev/null +++ b/src/tenvex/internal/scalar_impl.hpp @@ -0,0 +1,26 @@ +#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; +} + +} diff --git a/src/tenvex/internal/sub.h b/src/tenvex/internal/sub.h new file mode 100644 index 0000000..a762069 --- /dev/null +++ b/src/tenvex/internal/sub.h @@ -0,0 +1,28 @@ +#pragma once + +#include "expression.h" + +namespace tnvx +{ + +template +struct Sub : Expr> +{ + inline + Sub(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept; + + [[nodiscard]] inline + __m128 eval() const noexcept; + +private: + tnvx_type_storage _l; + tnvx_type_storage _r; +}; + +template +[[nodiscard]] inline +Sub operator-(const Expr& l, const Expr& r) noexcept; + +} + +#include "sub_impl.hpp" diff --git a/src/tenvex/internal/sub_impl.hpp b/src/tenvex/internal/sub_impl.hpp new file mode 100644 index 0000000..cbf092e --- /dev/null +++ b/src/tenvex/internal/sub_impl.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "sub.h" + +namespace tnvx +{ + +template +tnvx::Sub::Sub(const L& TNVX_RESTRICT l, const R& TNVX_RESTRICT r) noexcept + : _l(l), _r(r) +{ +} + +template +TNVX_INLINE +__m128 Sub::eval() const noexcept +{ + return _mm_sub_ps(_l.eval(), _r.eval()); +} + +template +TNVX_INLINE +Sub operator-(const Expr& l, const Expr& r) noexcept +{ + return { l.self(), r.self() }; +} + +} diff --git a/src/tenvex/tenvex.h b/src/tenvex/tenvex.h new file mode 100644 index 0000000..a859cf1 --- /dev/null +++ b/src/tenvex/tenvex.h @@ -0,0 +1,9 @@ +#pragma once + +#include "vec2.h" +#include "vec3.h" +#include "vec4.h" + +#include "internal/add.h" +#include "internal/sub.h" +#include "internal/mul.h" diff --git a/src/tenvex/vec2.h b/src/tenvex/vec2.h new file mode 100644 index 0000000..e86b3ca --- /dev/null +++ b/src/tenvex/vec2.h @@ -0,0 +1,10 @@ +#pragma once + +namespace tnvx +{ + +struct vec2 +{ +}; + +} // namespace tenvex diff --git a/src/tenvex/vec3.h b/src/tenvex/vec3.h new file mode 100644 index 0000000..55e22eb --- /dev/null +++ b/src/tenvex/vec3.h @@ -0,0 +1,10 @@ +#pragma once + +namespace tnvx +{ + +struct vec3 +{ +}; + +} // namespace tenvex diff --git a/src/tenvex/vec4.h b/src/tenvex/vec4.h new file mode 100644 index 0000000..98560d5 --- /dev/null +++ b/src/tenvex/vec4.h @@ -0,0 +1,36 @@ +#pragma once + +#include "internal/expression.h" + +#include + +namespace tnvx +{ + +struct alignas(16) vec4 : public Expr +{ + vec4() = default; + + vec4(float x, float y, float z, float w); + + template + vec4(const Expr& expr); + + template + vec4& operator=(const Expr& expr); + + __m128 eval() const noexcept; + + float magnitude() const noexcept; + vec4 norm() const noexcept; + + float x() const noexcept; + float y() const noexcept; + float z() const noexcept; + float w() const noexcept; + +private: + __m128 _data; +}; + +} // namespace tenvex