#ifndef GrVx_DEFINED
#define GrVx_DEFINED
#include "include/core/SkTypes.h"
#include "include/private/SkVx.h"
namespace pk {
namespace grvx {
#if defined(__clang__)
#pragma STDC FP_CONTRACT ON
#endif
template<int N> using vec = skvx::Vec<N, float>;
using float2 = vec<2>;
using float4 = vec<4>;
template<int N> using ivec = skvx::Vec<N, int32_t>;
using int2 = ivec<2>;
using int4 = ivec<4>;
template<int N> using uvec = skvx::Vec<N, uint32_t>;
using uint2 = uvec<2>;
using uint4 = uvec<4>;
static PK_ALWAYS_INLINE float dot(float2 a, float2 b) {
float2 ab = a*b;
return ab[0] + ab[1];
}
static PK_ALWAYS_INLINE float cross(float2 a, float2 b) {
float2 x = a*skvx::shuffle<1,0>(b);
return x[0] - x[1];
}
template<int N> PK_ALWAYS_INLINE vec<N> fast_madd(vec<N> f, vec<N> m, vec<N> a) {
#if FP_FAST_FMAF
return skvx::fma(f,m,a);
#else
return f*m + a;
#endif
}
#define GRVX_APPROX_ACOS_MAX_ERROR SkDegreesToRadians(.96f)
template<int N> PK_ALWAYS_INLINE vec<N> approx_acos(vec<N> x) {
constexpr static float a = -0.939115566365855f;
constexpr static float b = 0.9217841528914573f;
constexpr static float c = -1.2845906244690837f;
constexpr static float d = 0.295624144969963174f;
constexpr static float pi_over_2 = 1.5707963267948966f;
vec<N> xx = x*x;
vec<N> numer = fast_madd<N>(b,xx,a);
vec<N> denom = fast_madd<N>(xx, fast_madd<N>(d,xx,c), 1);
return fast_madd<N>(x, numer/denom, pi_over_2);
}
template<int Nx2>
PK_ALWAYS_INLINE vec<Nx2/2> approx_angle_between_vectors(vec<Nx2> a, vec<Nx2> b) {
auto aa=a*a, bb=b*b, ab=a*b;
auto cosTheta = (ab.lo + ab.hi) / skvx::sqrt((aa.lo + aa.hi) * (bb.lo + bb.hi));
cosTheta = skvx::max(skvx::min(1, cosTheta), -1);
return approx_acos(cosTheta);
}
template<typename T>
PK_ALWAYS_INLINE void strided_load4(const T* v, skvx::Vec<1,T>& a, skvx::Vec<1,T>& b,
skvx::Vec<1,T>& c, skvx::Vec<1,T>& d) {
a.val = v[0];
b.val = v[1];
c.val = v[2];
d.val = v[3];
}
template<int N, typename T>
PK_ALWAYS_INLINE typename std::enable_if<N >= 2, void>::type
strided_load4(const T* v, skvx::Vec<N,T>& a, skvx::Vec<N,T>& b, skvx::Vec<N,T>& c,
skvx::Vec<N,T>& d) {
strided_load4(v, a.lo, b.lo, c.lo, d.lo);
strided_load4(v + 4*(N/2), a.hi, b.hi, c.hi, d.hi);
}
#if !defined(SKNX_NO_SIMD)
#if defined(__ARM_NEON)
#define IMPL_LOAD4_TRANSPOSED(N, T, VLD) \
template<> \
PK_ALWAYS_INLINE void strided_load4(const T* v, skvx::Vec<N,T>& a, skvx::Vec<N,T>& b, \
skvx::Vec<N,T>& c, skvx::Vec<N,T>& d) { \
auto mat = VLD(v); \
a = skvx::bit_pun<skvx::Vec<N,T>>(mat.val[0]); \
b = skvx::bit_pun<skvx::Vec<N,T>>(mat.val[1]); \
c = skvx::bit_pun<skvx::Vec<N,T>>(mat.val[2]); \
d = skvx::bit_pun<skvx::Vec<N,T>>(mat.val[3]); \
}
IMPL_LOAD4_TRANSPOSED(2, uint32_t, vld4_u32);
IMPL_LOAD4_TRANSPOSED(4, uint16_t, vld4_u16);
IMPL_LOAD4_TRANSPOSED(8, uint8_t, vld4_u8);
IMPL_LOAD4_TRANSPOSED(2, int32_t, vld4_s32);
IMPL_LOAD4_TRANSPOSED(4, int16_t, vld4_s16);
IMPL_LOAD4_TRANSPOSED(8, int8_t, vld4_s8);
IMPL_LOAD4_TRANSPOSED(2, float, vld4_f32);
IMPL_LOAD4_TRANSPOSED(4, uint32_t, vld4q_u32);
IMPL_LOAD4_TRANSPOSED(8, uint16_t, vld4q_u16);
IMPL_LOAD4_TRANSPOSED(16, uint8_t, vld4q_u8);
IMPL_LOAD4_TRANSPOSED(4, int32_t, vld4q_s32);
IMPL_LOAD4_TRANSPOSED(8, int16_t, vld4q_s16);
IMPL_LOAD4_TRANSPOSED(16, int8_t, vld4q_s8);
IMPL_LOAD4_TRANSPOSED(4, float, vld4q_f32);
#undef IMPL_LOAD4_TRANSPOSED
#elif defined(__SSE__)
template<>
PK_ALWAYS_INLINE void strided_load4(const float* v, float4& a, float4& b, float4& c, float4& d) {
using skvx::bit_pun;
__m128 a_ = _mm_loadu_ps(v);
__m128 b_ = _mm_loadu_ps(v+4);
__m128 c_ = _mm_loadu_ps(v+8);
__m128 d_ = _mm_loadu_ps(v+12);
_MM_TRANSPOSE4_PS(a_, b_, c_, d_);
a = bit_pun<float4>(a_);
b = bit_pun<float4>(b_);
c = bit_pun<float4>(c_);
d = bit_pun<float4>(d_);
}
#endif
#endif
template<typename T>
PK_ALWAYS_INLINE void strided_load2(const T* v, skvx::Vec<1,T>& a, skvx::Vec<1,T>& b) {
a.val = v[0];
b.val = v[1];
}
template<int N, typename T>
PK_ALWAYS_INLINE typename std::enable_if<N >= 2, void>::type
strided_load2(const T* v, skvx::Vec<N,T>& a, skvx::Vec<N,T>& b) {
strided_load2(v, a.lo, b.lo);
strided_load2(v + 2*(N/2), a.hi, b.hi);
}
#if !defined(SKNX_NO_SIMD)
#if defined(__ARM_NEON)
#define IMPL_LOAD2_TRANSPOSED(N, T, VLD) \
template<> \
PK_ALWAYS_INLINE void strided_load2(const T* v, skvx::Vec<N,T>& a, skvx::Vec<N,T>& b) { \
auto mat = VLD(v); \
a = skvx::bit_pun<skvx::Vec<N,T>>(mat.val[0]); \
b = skvx::bit_pun<skvx::Vec<N,T>>(mat.val[1]); \
}
IMPL_LOAD2_TRANSPOSED(2, uint32_t, vld2_u32);
IMPL_LOAD2_TRANSPOSED(4, uint16_t, vld2_u16);
IMPL_LOAD2_TRANSPOSED(8, uint8_t, vld2_u8);
IMPL_LOAD2_TRANSPOSED(2, int32_t, vld2_s32);
IMPL_LOAD2_TRANSPOSED(4, int16_t, vld2_s16);
IMPL_LOAD2_TRANSPOSED(8, int8_t, vld2_s8);
IMPL_LOAD2_TRANSPOSED(2, float, vld2_f32);
IMPL_LOAD2_TRANSPOSED(4, uint32_t, vld2q_u32);
IMPL_LOAD2_TRANSPOSED(8, uint16_t, vld2q_u16);
IMPL_LOAD2_TRANSPOSED(16, uint8_t, vld2q_u8);
IMPL_LOAD2_TRANSPOSED(4, int32_t, vld2q_s32);
IMPL_LOAD2_TRANSPOSED(8, int16_t, vld2q_s16);
IMPL_LOAD2_TRANSPOSED(16, int8_t, vld2q_s8);
IMPL_LOAD2_TRANSPOSED(4, float, vld2q_f32);
#undef IMPL_LOAD2_TRANSPOSED
#endif
#endif
#if defined(__clang__)
#pragma STDC FP_CONTRACT DEFAULT
#endif
} }
#endif