mtklein | 3e490b7 | 2015-03-20 06:33:02 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2015 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #ifndef SkNx_DEFINED |
| 9 | #define SkNx_DEFINED |
| 10 | |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 11 | |
| 12 | #define SKNX_NO_SIMDx // Remove the x to disable SIMD for all SkNx types. |
| 13 | |
| 14 | |
| 15 | #include "SkScalar.h" |
| 16 | #include "SkTypes.h" |
| 17 | #include <math.h> |
| 18 | #define REQUIRE(x) static_assert(x, #x) |
| 19 | |
mtklein | 082e329 | 2015-08-12 11:56:43 -0700 | [diff] [blame] | 20 | // This file may be included multiple times by .cpp files with different flags, leading |
| 21 | // to different definitions. Usually that doesn't matter because it's all inlined, but |
| 22 | // in Debug modes the compilers may not inline everything. So wrap everything in an |
| 23 | // anonymous namespace to give each includer their own silo of this code (or the linker |
| 24 | // will probably pick one randomly for us, which is rarely correct). |
| 25 | namespace { |
| 26 | |
mtklein | 8fe8fff | 2015-04-14 11:49:14 -0700 | [diff] [blame] | 27 | // The default implementations just fall back on a pair of size N/2. |
| 28 | |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 29 | template <int N, typename T> |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 30 | class SkNx { |
mtklein | 115acee | 2015-04-14 14:02:52 -0700 | [diff] [blame] | 31 | public: |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 32 | SkNx() {} |
| 33 | SkNx(const SkNx<N/2, T>& lo, const SkNx<N/2, T>& hi) : fLo(lo), fHi(hi) {} |
| 34 | SkNx(T val) : fLo(val), fHi(val) {} |
| 35 | static SkNx Load(const T vals[N]) { |
| 36 | return SkNx(SkNx<N/2,T>::Load(vals), SkNx<N/2,T>::Load(vals+N/2)); |
mtklein | 115acee | 2015-04-14 14:02:52 -0700 | [diff] [blame] | 37 | } |
| 38 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 39 | SkNx(T a, T b) : fLo(a), fHi(b) { REQUIRE(N==2); } |
| 40 | SkNx(T a, T b, T c, T d) : fLo(a,b), fHi(c,d) { REQUIRE(N==4); } |
| 41 | SkNx(T a, T b, T c, T d, T e, T f, T g, T h) : fLo(a,b,c,d), fHi(e,f,g,h) { REQUIRE(N==8); } |
| 42 | SkNx(T a, T b, T c, T d, T e, T f, T g, T h, |
mtklein | d2ffd36 | 2015-05-12 06:11:21 -0700 | [diff] [blame] | 43 | T i, T j, T k, T l, T m, T n, T o, T p) |
| 44 | : fLo(a,b,c,d, e,f,g,h), fHi(i,j,k,l, m,n,o,p) { REQUIRE(N==16); } |
mtklein | 115acee | 2015-04-14 14:02:52 -0700 | [diff] [blame] | 45 | |
| 46 | void store(T vals[N]) const { |
| 47 | fLo.store(vals); |
| 48 | fHi.store(vals+N/2); |
| 49 | } |
| 50 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 51 | SkNx saturatedAdd(const SkNx& o) const { |
| 52 | return SkNx(fLo.saturatedAdd(o.fLo), fHi.saturatedAdd(o.fHi)); |
mtklein | 6cbf18c | 2015-05-12 15:48:09 -0700 | [diff] [blame] | 53 | } |
| 54 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 55 | SkNx operator + (const SkNx& o) const { return SkNx(fLo + o.fLo, fHi + o.fHi); } |
| 56 | SkNx operator - (const SkNx& o) const { return SkNx(fLo - o.fLo, fHi - o.fHi); } |
| 57 | SkNx operator * (const SkNx& o) const { return SkNx(fLo * o.fLo, fHi * o.fHi); } |
mtklein | 115acee | 2015-04-14 14:02:52 -0700 | [diff] [blame] | 58 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 59 | SkNx operator << (int bits) const { return SkNx(fLo << bits, fHi << bits); } |
| 60 | SkNx operator >> (int bits) const { return SkNx(fLo >> bits, fHi >> bits); } |
mtklein | 115acee | 2015-04-14 14:02:52 -0700 | [diff] [blame] | 61 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 62 | static SkNx Min(const SkNx& a, const SkNx& b) { |
| 63 | return SkNx(SkNx<N/2, T>::Min(a.fLo, b.fLo), SkNx<N/2, T>::Min(a.fHi, b.fHi)); |
mtklein | 27e517a | 2015-05-14 17:53:04 -0700 | [diff] [blame] | 64 | } |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 65 | SkNx operator < (const SkNx& o) const { return SkNx(fLo < o.fLo, fHi < o.fHi); } |
mtklein | 115acee | 2015-04-14 14:02:52 -0700 | [diff] [blame] | 66 | |
| 67 | template <int k> T kth() const { |
| 68 | SkASSERT(0 <= k && k < N); |
| 69 | return k < N/2 ? fLo.template kth<k>() : fHi.template kth<k-N/2>(); |
| 70 | } |
| 71 | |
mtklein | b5e8611 | 2015-06-24 15:18:39 -0700 | [diff] [blame] | 72 | bool allTrue() const { return fLo.allTrue() && fHi.allTrue(); } |
| 73 | bool anyTrue() const { return fLo.anyTrue() || fHi.anyTrue(); } |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 74 | SkNx thenElse(const SkNx& t, const SkNx& e) const { |
| 75 | return SkNx(fLo.thenElse(t.fLo, e.fLo), fHi.thenElse(t.fHi, e.fHi)); |
mtklein | b5e8611 | 2015-06-24 15:18:39 -0700 | [diff] [blame] | 76 | } |
| 77 | |
mtklein | d2ffd36 | 2015-05-12 06:11:21 -0700 | [diff] [blame] | 78 | protected: |
mtklein | 115acee | 2015-04-14 14:02:52 -0700 | [diff] [blame] | 79 | REQUIRE(0 == (N & (N-1))); |
mtklein | 115acee | 2015-04-14 14:02:52 -0700 | [diff] [blame] | 80 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 81 | SkNx<N/2, T> fLo, fHi; |
mtklein | 115acee | 2015-04-14 14:02:52 -0700 | [diff] [blame] | 82 | }; |
| 83 | |
mtklein | 6f79709 | 2015-11-09 08:33:53 -0800 | [diff] [blame] | 84 | template <int N> |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 85 | class SkNx<N,float> { |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 86 | public: |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 87 | SkNx() {} |
| 88 | SkNx(float val) : fLo(val), fHi(val) {} |
| 89 | static SkNx Load(const float vals[N]) { |
| 90 | return SkNx(SkNx<N/2, float>::Load(vals), SkNx<N/2, float>::Load(vals+N/2)); |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 91 | } |
mtklein | aba1dc8 | 2015-08-31 14:39:59 -0700 | [diff] [blame] | 92 | // FromBytes() and toBytes() specializations may assume their argument is N-byte aligned. |
| 93 | // E.g. Sk4f::FromBytes() may assume it's reading from a 4-byte-aligned pointer. |
| 94 | // Converts [0,255] bytes to [0.0, 255.0] floats. |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 95 | static SkNx FromBytes(const uint8_t bytes[N]) { |
| 96 | return SkNx(SkNx<N/2, float>::FromBytes(bytes), SkNx<N/2, float>::FromBytes(bytes+N/2)); |
mtklein | aba1dc8 | 2015-08-31 14:39:59 -0700 | [diff] [blame] | 97 | } |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 98 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 99 | SkNx(float a, float b) : fLo(a), fHi(b) { REQUIRE(N==2); } |
| 100 | SkNx(float a, float b, float c, float d) : fLo(a,b), fHi(c,d) { REQUIRE(N==4); } |
| 101 | SkNx(float a, float b, float c, float d, float e, float f, float g, float h) |
mtklein | 6f79709 | 2015-11-09 08:33:53 -0800 | [diff] [blame] | 102 | : fLo(a,b,c,d) |
| 103 | , fHi(e,f,g,h) { REQUIRE(N==8); } |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 104 | |
mtklein | 6f79709 | 2015-11-09 08:33:53 -0800 | [diff] [blame] | 105 | void store(float vals[N]) const { |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 106 | fLo.store(vals); |
| 107 | fHi.store(vals+N/2); |
| 108 | } |
mtklein | aba1dc8 | 2015-08-31 14:39:59 -0700 | [diff] [blame] | 109 | // Please see note on FromBytes(). |
mtklein | a508f3c | 2015-09-01 06:29:45 -0700 | [diff] [blame] | 110 | // Clamps to [0.0,255.0] floats and truncates to [0,255] bytes. |
mtklein | aba1dc8 | 2015-08-31 14:39:59 -0700 | [diff] [blame] | 111 | void toBytes(uint8_t bytes[N]) const { |
| 112 | fLo.toBytes(bytes); |
| 113 | fHi.toBytes(bytes+N/2); |
| 114 | } |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 115 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 116 | SkNx operator + (const SkNx& o) const { return SkNx(fLo + o.fLo, fHi + o.fHi); } |
| 117 | SkNx operator - (const SkNx& o) const { return SkNx(fLo - o.fLo, fHi - o.fHi); } |
| 118 | SkNx operator * (const SkNx& o) const { return SkNx(fLo * o.fLo, fHi * o.fHi); } |
| 119 | SkNx operator / (const SkNx& o) const { return SkNx(fLo / o.fLo, fHi / o.fHi); } |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 120 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 121 | SkNx operator == (const SkNx& o) const { return SkNx(fLo == o.fLo, fHi == o.fHi); } |
| 122 | SkNx operator != (const SkNx& o) const { return SkNx(fLo != o.fLo, fHi != o.fHi); } |
| 123 | SkNx operator < (const SkNx& o) const { return SkNx(fLo < o.fLo, fHi < o.fHi); } |
| 124 | SkNx operator > (const SkNx& o) const { return SkNx(fLo > o.fLo, fHi > o.fHi); } |
| 125 | SkNx operator <= (const SkNx& o) const { return SkNx(fLo <= o.fLo, fHi <= o.fHi); } |
| 126 | SkNx operator >= (const SkNx& o) const { return SkNx(fLo >= o.fLo, fHi >= o.fHi); } |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 127 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 128 | static SkNx Min(const SkNx& l, const SkNx& r) { |
| 129 | return SkNx(SkNx<N/2, float>::Min(l.fLo, r.fLo), SkNx<N/2, float>::Min(l.fHi, r.fHi)); |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 130 | } |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 131 | static SkNx Max(const SkNx& l, const SkNx& r) { |
| 132 | return SkNx(SkNx<N/2, float>::Max(l.fLo, r.fLo), SkNx<N/2, float>::Max(l.fHi, r.fHi)); |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 133 | } |
| 134 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 135 | SkNx sqrt() const { return SkNx(fLo. sqrt(), fHi. sqrt()); } |
mtklein | d7c014f | 2015-04-27 14:22:32 -0700 | [diff] [blame] | 136 | |
| 137 | // Generally, increasing precision, increasing cost. |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 138 | SkNx rsqrt0() const { return SkNx(fLo.rsqrt0(), fHi.rsqrt0()); } |
| 139 | SkNx rsqrt1() const { return SkNx(fLo.rsqrt1(), fHi.rsqrt1()); } |
| 140 | SkNx rsqrt2() const { return SkNx(fLo.rsqrt2(), fHi.rsqrt2()); } |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 141 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 142 | SkNx invert() const { return SkNx(fLo. invert(), fHi. invert()); } |
| 143 | SkNx approxInvert() const { return SkNx(fLo.approxInvert(), fHi.approxInvert()); } |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 144 | |
mtklein | 6f79709 | 2015-11-09 08:33:53 -0800 | [diff] [blame] | 145 | template <int k> float kth() const { |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 146 | SkASSERT(0 <= k && k < N); |
mtklein | a156a8f | 2015-04-03 06:16:13 -0700 | [diff] [blame] | 147 | return k < N/2 ? fLo.template kth<k>() : fHi.template kth<k-N/2>(); |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 148 | } |
| 149 | |
mtklein | b5e8611 | 2015-06-24 15:18:39 -0700 | [diff] [blame] | 150 | bool allTrue() const { return fLo.allTrue() && fHi.allTrue(); } |
| 151 | bool anyTrue() const { return fLo.anyTrue() || fHi.anyTrue(); } |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 152 | SkNx thenElse(const SkNx& t, const SkNx& e) const { |
| 153 | return SkNx(fLo.thenElse(t.fLo, e.fLo), fHi.thenElse(t.fHi, e.fHi)); |
mtklein | b5e8611 | 2015-06-24 15:18:39 -0700 | [diff] [blame] | 154 | } |
| 155 | |
mtklein | d2ffd36 | 2015-05-12 06:11:21 -0700 | [diff] [blame] | 156 | protected: |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 157 | REQUIRE(0 == (N & (N-1))); |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 158 | SkNx(const SkNx<N/2, float>& lo, const SkNx<N/2, float>& hi) : fLo(lo), fHi(hi) {} |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 159 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 160 | SkNx<N/2, float> fLo, fHi; |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 161 | }; |
| 162 | |
| 163 | |
mtklein | 8fe8fff | 2015-04-14 11:49:14 -0700 | [diff] [blame] | 164 | // Bottom out the default implementations with scalars when nothing's been specialized. |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 165 | |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 166 | template <typename T> |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 167 | class SkNx<1,T> { |
mtklein | 115acee | 2015-04-14 14:02:52 -0700 | [diff] [blame] | 168 | public: |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 169 | SkNx() {} |
| 170 | SkNx(T val) : fVal(val) {} |
| 171 | static SkNx Load(const T vals[1]) { return SkNx(vals[0]); } |
mtklein | 115acee | 2015-04-14 14:02:52 -0700 | [diff] [blame] | 172 | |
| 173 | void store(T vals[1]) const { vals[0] = fVal; } |
| 174 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 175 | SkNx saturatedAdd(const SkNx& o) const { |
mtklein | 6cbf18c | 2015-05-12 15:48:09 -0700 | [diff] [blame] | 176 | SkASSERT((T)(~0) > 0); // TODO: support signed T |
| 177 | T sum = fVal + o.fVal; |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 178 | return SkNx(sum < fVal ? (T)(~0) : sum); |
mtklein | 6cbf18c | 2015-05-12 15:48:09 -0700 | [diff] [blame] | 179 | } |
| 180 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 181 | SkNx operator + (const SkNx& o) const { return SkNx(fVal + o.fVal); } |
| 182 | SkNx operator - (const SkNx& o) const { return SkNx(fVal - o.fVal); } |
| 183 | SkNx operator * (const SkNx& o) const { return SkNx(fVal * o.fVal); } |
mtklein | 115acee | 2015-04-14 14:02:52 -0700 | [diff] [blame] | 184 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 185 | SkNx operator << (int bits) const { return SkNx(fVal << bits); } |
| 186 | SkNx operator >> (int bits) const { return SkNx(fVal >> bits); } |
mtklein | 115acee | 2015-04-14 14:02:52 -0700 | [diff] [blame] | 187 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 188 | static SkNx Min(const SkNx& a, const SkNx& b) { return SkNx(SkTMin(a.fVal, b.fVal)); } |
| 189 | SkNx operator <(const SkNx& o) const { return SkNx(fVal < o.fVal); } |
mtklein | 27e517a | 2015-05-14 17:53:04 -0700 | [diff] [blame] | 190 | |
mtklein | 115acee | 2015-04-14 14:02:52 -0700 | [diff] [blame] | 191 | template <int k> T kth() const { |
| 192 | SkASSERT(0 == k); |
| 193 | return fVal; |
| 194 | } |
| 195 | |
mtklein | b5e8611 | 2015-06-24 15:18:39 -0700 | [diff] [blame] | 196 | bool allTrue() const { return fVal; } |
| 197 | bool anyTrue() const { return fVal; } |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 198 | SkNx thenElse(const SkNx& t, const SkNx& e) const { return fVal ? t : e; } |
mtklein | b5e8611 | 2015-06-24 15:18:39 -0700 | [diff] [blame] | 199 | |
mtklein | d2ffd36 | 2015-05-12 06:11:21 -0700 | [diff] [blame] | 200 | protected: |
mtklein | 115acee | 2015-04-14 14:02:52 -0700 | [diff] [blame] | 201 | T fVal; |
| 202 | }; |
| 203 | |
mtklein | 6f79709 | 2015-11-09 08:33:53 -0800 | [diff] [blame] | 204 | template <> |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 205 | class SkNx<1,float> { |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 206 | public: |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 207 | SkNx() {} |
| 208 | SkNx(float val) : fVal(val) {} |
| 209 | static SkNx Load(const float vals[1]) { return SkNx(vals[0]); } |
| 210 | static SkNx FromBytes(const uint8_t bytes[1]) { return SkNx((float)bytes[0]); } |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 211 | |
mtklein | 6f79709 | 2015-11-09 08:33:53 -0800 | [diff] [blame] | 212 | void store(float vals[1]) const { vals[0] = fVal; } |
| 213 | void toBytes(uint8_t bytes[1]) const { bytes[0] = (uint8_t)(SkTMin(fVal, 255.0f)); } |
mtklein | 1113da7 | 2015-04-27 12:08:01 -0700 | [diff] [blame] | 214 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 215 | SkNx operator + (const SkNx& o) const { return SkNx(fVal + o.fVal); } |
| 216 | SkNx operator - (const SkNx& o) const { return SkNx(fVal - o.fVal); } |
| 217 | SkNx operator * (const SkNx& o) const { return SkNx(fVal * o.fVal); } |
| 218 | SkNx operator / (const SkNx& o) const { return SkNx(fVal / o.fVal); } |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 219 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 220 | SkNx operator == (const SkNx& o) const { return SkNx(fVal == o.fVal); } |
| 221 | SkNx operator != (const SkNx& o) const { return SkNx(fVal != o.fVal); } |
| 222 | SkNx operator < (const SkNx& o) const { return SkNx(fVal < o.fVal); } |
| 223 | SkNx operator > (const SkNx& o) const { return SkNx(fVal > o.fVal); } |
| 224 | SkNx operator <= (const SkNx& o) const { return SkNx(fVal <= o.fVal); } |
| 225 | SkNx operator >= (const SkNx& o) const { return SkNx(fVal >= o.fVal); } |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 226 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 227 | static SkNx Min(const SkNx& l, const SkNx& r) { return SkNx(SkTMin(l.fVal, r.fVal)); } |
| 228 | static SkNx Max(const SkNx& l, const SkNx& r) { return SkNx(SkTMax(l.fVal, r.fVal)); } |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 229 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 230 | SkNx sqrt() const { return SkNx(sqrtf(fVal)); } |
| 231 | SkNx rsqrt0() const { return SkNx(1.0f / sqrtf(fVal)); } |
| 232 | SkNx rsqrt1() const { return this->rsqrt0(); } |
| 233 | SkNx rsqrt2() const { return this->rsqrt1(); } |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 234 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 235 | SkNx invert() const { return SkNx(1.0f / fVal); } |
| 236 | SkNx approxInvert() const { return this->invert(); } |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 237 | |
mtklein | 6f79709 | 2015-11-09 08:33:53 -0800 | [diff] [blame] | 238 | template <int k> float kth() const { |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 239 | SkASSERT(k == 0); |
| 240 | return fVal; |
| 241 | } |
| 242 | |
mtklein | 6f79709 | 2015-11-09 08:33:53 -0800 | [diff] [blame] | 243 | bool allTrue() const { return this->pun() != 0; } |
| 244 | bool anyTrue() const { return this->pun() != 0; } |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 245 | SkNx thenElse(const SkNx& t, const SkNx& e) const { return this->pun() ? t : e; } |
mtklein | b5e8611 | 2015-06-24 15:18:39 -0700 | [diff] [blame] | 246 | |
mtklein | d2ffd36 | 2015-05-12 06:11:21 -0700 | [diff] [blame] | 247 | protected: |
mtklein | 6f79709 | 2015-11-09 08:33:53 -0800 | [diff] [blame] | 248 | uint32_t pun() const { |
| 249 | union { float f; uint32_t i; } pun = { fVal }; |
mtklein | b5e8611 | 2015-06-24 15:18:39 -0700 | [diff] [blame] | 250 | return pun.i; |
| 251 | } |
| 252 | |
mtklein | 6f79709 | 2015-11-09 08:33:53 -0800 | [diff] [blame] | 253 | float fVal; |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 254 | }; |
| 255 | |
mtklein | a1c0ee4 | 2015-09-10 14:16:07 -0700 | [diff] [blame] | 256 | // This default implementation can be specialized by ../opts/SkNx_foo.h |
| 257 | // if there's a better platform-specific shuffle strategy. |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 258 | template <typename Nx, int... Ix> |
| 259 | inline Nx SkNx_shuffle_impl(const Nx& src) { return Nx( src.template kth<Ix>()... ); } |
mtklein | a1c0ee4 | 2015-09-10 14:16:07 -0700 | [diff] [blame] | 260 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 261 | // This generic shuffle can be called with 1 or N indices: |
mtklein | a1c0ee4 | 2015-09-10 14:16:07 -0700 | [diff] [blame] | 262 | // Sk4f f(a,b,c,d); |
| 263 | // SkNx_shuffle<3>(f); // ~~~> Sk4f(d,d,d,d) |
| 264 | // SkNx_shuffle<2,1,0,3>(f); // ~~~> Sk4f(c,b,a,d) |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 265 | template <int... Ix, typename Nx> |
| 266 | inline Nx SkNx_shuffle(const Nx& src) { return SkNx_shuffle_impl<Nx, Ix...>(src); } |
mtklein | a1c0ee4 | 2015-09-10 14:16:07 -0700 | [diff] [blame] | 267 | |
| 268 | // A reminder alias that shuffles can be used to duplicate a single index across a vector. |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 269 | template <int Ix, typename Nx> |
| 270 | inline Nx SkNx_dup(const Nx& src) { return SkNx_shuffle<Ix>(src); } |
| 271 | |
| 272 | // This is a poor-man's std::make_index_sequence from C++14. |
| 273 | // I'd implement it fully, but it hurts my head. |
| 274 | template <int...> struct SkIntSequence {}; |
| 275 | template <int N> struct MakeSkIntSequence; |
| 276 | template <> struct MakeSkIntSequence< 1> : SkIntSequence<0 >{}; |
| 277 | template <> struct MakeSkIntSequence< 2> : SkIntSequence<0,1 >{}; |
| 278 | template <> struct MakeSkIntSequence< 4> : SkIntSequence<0,1,2,3 >{}; |
| 279 | template <> struct MakeSkIntSequence< 8> : SkIntSequence<0,1,2,3,4,5,6,7 >{}; |
| 280 | template <> struct MakeSkIntSequence<16> : SkIntSequence<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>{}; |
| 281 | |
| 282 | // This is the default/fallback implementation for SkNx_cast. Best to specialize SkNx_cast! |
| 283 | template <typename D, typename S, int N, int... Ix> |
| 284 | SkNx<N,D> SkNx_cast_fallback(const SkNx<N,S>& src, SkIntSequence<Ix...>) { |
| 285 | return SkNx<N,D>( (D)src.template kth<Ix>()... ); |
| 286 | } |
| 287 | |
| 288 | // This is a generic cast between two SkNx with the same number of elements N. E.g. |
| 289 | // Sk4b bs = ...; // Load 4 bytes. |
| 290 | // Sk4f fs = SkNx_cast<float>(bs); // (This will replace SkNf::FromBytes() one day.) |
| 291 | // Sk4i is = SkNx_cast<int>(fs); // Cast each float to int. |
| 292 | // This can be specialized in ../opts/SkNx_foo.h if there's a better platform-specific cast. |
| 293 | template <typename D, typename S, int N> |
| 294 | SkNx<N,D> SkNx_cast(const SkNx<N,S>& src) { |
| 295 | return SkNx_cast_fallback<D,S,N>(src, MakeSkIntSequence<N>()); |
| 296 | } |
mtklein | a1c0ee4 | 2015-09-10 14:16:07 -0700 | [diff] [blame] | 297 | |
mtklein | 082e329 | 2015-08-12 11:56:43 -0700 | [diff] [blame] | 298 | } // namespace |
| 299 | |
mtklein | a1c0ee4 | 2015-09-10 14:16:07 -0700 | [diff] [blame] | 300 | |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 301 | // Include platform specific specializations if available. |
| 302 | #ifndef SKNX_NO_SIMD |
mtklein | 084db25 | 2015-11-11 11:39:09 -0800 | [diff] [blame] | 303 | #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_AVX |
| 304 | #include "../opts/SkNx_avx.h" |
| 305 | #elif SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 306 | #include "../opts/SkNx_sse.h" |
| 307 | #elif defined(SK_ARM_HAS_NEON) |
| 308 | #include "../opts/SkNx_neon.h" |
| 309 | #endif |
| 310 | #endif |
| 311 | |
| 312 | #undef REQUIRE |
| 313 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 314 | typedef SkNx<2, float> Sk2f; |
| 315 | typedef SkNx<2, float> Sk2s; |
| 316 | typedef SkNx<4, float> Sk4f; |
| 317 | typedef SkNx<4, float> Sk4s; |
| 318 | typedef SkNx<8, float> Sk8f; |
| 319 | typedef SkNx<8, float> Sk8s; |
mtklein | c9adb05 | 2015-03-30 10:50:27 -0700 | [diff] [blame] | 320 | |
mtklein | 6c221b4 | 2015-11-20 13:53:19 -0800 | [diff] [blame^] | 321 | typedef SkNx<8, uint16_t> Sk8h; |
| 322 | typedef SkNx<16, uint16_t> Sk16h; |
| 323 | typedef SkNx<16, uint8_t> Sk16b; |
| 324 | |
| 325 | typedef SkNx<4, int> Sk4i; |
mtklein | 1113da7 | 2015-04-27 12:08:01 -0700 | [diff] [blame] | 326 | |
mtklein | 3e490b7 | 2015-03-20 06:33:02 -0700 | [diff] [blame] | 327 | #endif//SkNx_DEFINED |