Sk4px
Xfermode_SrcOver:
SSE: 2.08ms -> 2.03ms (~2% faster)
NEON: my N5 is noisy, but there appears to be no perf change
BUG=skia:
Review URL: https://codereview.chromium.org/1132273004
diff --git a/src/core/Sk4px.h b/src/core/Sk4px.h
new file mode 100644
index 0000000..3d2a8e3
--- /dev/null
+++ b/src/core/Sk4px.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef Sk4px_DEFINED
+#define Sk4px_DEFINED
+
+#include "SkNx.h"
+#include "SkColor.h"
+
+// 1, 2 or 4 SkPMColors, generally vectorized.
+class Sk4px : public Sk16b {
+public:
+ Sk4px(SkPMColor); // Duplicate 4x.
+ Sk4px(const Sk16b& v) : Sk16b(v) {}
+
+ // When loading or storing fewer than 4 SkPMColors, we use the low lanes.
+ static Sk4px Load4(const SkPMColor[4]);
+ static Sk4px Load2(const SkPMColor[2]);
+ static Sk4px Load1(const SkPMColor[1]);
+
+ void store4(SkPMColor[4]) const;
+ void store2(SkPMColor[2]) const;
+ void store1(SkPMColor[1]) const;
+
+ // 1, 2, or 4 SkPMColors with 16-bit components.
+ // This is most useful as the result of a multiply, e.g. from mulWiden().
+ class Wide : public Sk16h {
+ public:
+ Wide(const Sk16h& v) : Sk16h(v) {}
+
+ // Pack the top byte of each component back down into 4 SkPMColors.
+ Sk4px addNarrowHi(const Sk16h&) const;
+ private:
+ typedef Sk16h INHERITED;
+ };
+
+ Wide widenLo() const; // ARGB -> 0A 0R 0G 0B
+ Wide widenHi() const; // ARGB -> A0 R0 G0 B0
+ Wide mulWiden(const Sk16b&) const; // 8-bit x 8-bit -> 16-bit components.
+
+ // A generic driver that maps fn over a src array into a dst array.
+ // fn should take an Sk4px (4 src pixels) and return an Sk4px (4 dst pixels).
+ template <typename Fn>
+ static void MapSrc(int count, SkPMColor* dst, const SkPMColor* src, Fn fn) {
+ // This looks a bit odd, but it helps loop-invariant hoisting across different calls to fn.
+ // Basically, we need to make sure we keep things inside a single loop.
+ while (count > 0) {
+ if (count >= 8) {
+ Sk4px dst0 = fn(Load4(src+0)),
+ dst4 = fn(Load4(src+4));
+ dst0.store4(dst+0);
+ dst4.store4(dst+4);
+ dst += 8; src += 8; count -= 8;
+ continue; // Keep our stride at 8 pixels as long as possible.
+ }
+ SkASSERT(count <= 7);
+ if (count >= 4) {
+ fn(Load4(src)).store4(dst);
+ dst += 4; src += 4; count -= 4;
+ }
+ if (count >= 2) {
+ fn(Load2(src)).store2(dst);
+ dst += 2; src += 2; count -= 2;
+ }
+ if (count >= 1) {
+ fn(Load1(src)).store1(dst);
+ }
+ break;
+ }
+ }
+
+private:
+ typedef Sk16b INHERITED;
+};
+
+#ifdef SKNX_NO_SIMD
+ #include "../opts/Sk4px_none.h"
+#else
+ #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
+ #include "../opts/Sk4px_SSE2.h"
+ #elif defined(SK_ARM_HAS_NEON)
+ #include "../opts/Sk4px_NEON.h"
+ #else
+ #include "../opts/Sk4px_none.h"
+ #endif
+#endif
+
+#endif//Sk4px_DEFINED
diff --git a/src/core/SkBlitRow_D32.cpp b/src/core/SkBlitRow_D32.cpp
index de99894..f5cb45d 100644
--- a/src/core/SkBlitRow_D32.cpp
+++ b/src/core/SkBlitRow_D32.cpp
@@ -131,6 +131,8 @@
return proc;
}
+#include "Sk4px.h"
+
// Color32 uses the blend_256_round_alt algorithm from tests/BlendTest.cpp.
// It's not quite perfect, but it's never wrong in the interesting edge cases,
// and it's quite a bit faster than blend_perfect.
@@ -146,94 +148,10 @@
invA += invA >> 7;
SkASSERT(invA < 256); // We've already handled alpha == 0 above.
-#if defined(SK_ARM_HAS_NEON)
- uint16x8_t colorHigh = vshll_n_u8((uint8x8_t)vdup_n_u32(color), 8);
- uint16x8_t colorAndRound = vaddq_u16(colorHigh, vdupq_n_u16(128));
- uint8x8_t invA8 = vdup_n_u8(invA);
+ Sk16h colorHighAndRound = Sk4px(color).widenHi() + Sk16h(128);
+ Sk16b invA_16x(invA);
- // Does the core work of blending color onto 4 pixels, returning the resulting 4 pixels.
- auto kernel = [&](const uint32x4_t& src4) -> uint32x4_t {
- uint16x8_t lo = vmull_u8(vget_low_u8( (uint8x16_t)src4), invA8),
- hi = vmull_u8(vget_high_u8((uint8x16_t)src4), invA8);
- return (uint32x4_t)
- vcombine_u8(vaddhn_u16(colorAndRound, lo), vaddhn_u16(colorAndRound, hi));
- };
-
- while (count >= 8) {
- uint32x4_t dst0 = kernel(vld1q_u32(src+0)),
- dst4 = kernel(vld1q_u32(src+4));
- vst1q_u32(dst+0, dst0);
- vst1q_u32(dst+4, dst4);
- src += 8;
- dst += 8;
- count -= 8;
- }
- if (count >= 4) {
- vst1q_u32(dst, kernel(vld1q_u32(src)));
- src += 4;
- dst += 4;
- count -= 4;
- }
- if (count >= 2) {
- uint32x2_t src2 = vld1_u32(src);
- vst1_u32(dst, vget_low_u32(kernel(vcombine_u32(src2, src2))));
- src += 2;
- dst += 2;
- count -= 2;
- }
- if (count >= 1) {
- vst1q_lane_u32(dst, kernel(vdupq_n_u32(*src)), 0);
- }
-
-#elif SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
- __m128i colorHigh = _mm_unpacklo_epi8(_mm_setzero_si128(), _mm_set1_epi32(color));
- __m128i colorAndRound = _mm_add_epi16(colorHigh, _mm_set1_epi16(128));
- __m128i invA16 = _mm_set1_epi16(invA);
-
- // Does the core work of blending color onto 4 pixels, returning the resulting 4 pixels.
- auto kernel = [&](const __m128i& src4) -> __m128i {
- __m128i lo = _mm_mullo_epi16(invA16, _mm_unpacklo_epi8(src4, _mm_setzero_si128())),
- hi = _mm_mullo_epi16(invA16, _mm_unpackhi_epi8(src4, _mm_setzero_si128()));
- return _mm_packus_epi16(_mm_srli_epi16(_mm_add_epi16(colorAndRound, lo), 8),
- _mm_srli_epi16(_mm_add_epi16(colorAndRound, hi), 8));
- };
-
- while (count >= 8) {
- __m128i dst0 = kernel(_mm_loadu_si128((const __m128i*)(src+0))),
- dst4 = kernel(_mm_loadu_si128((const __m128i*)(src+4)));
- _mm_storeu_si128((__m128i*)(dst+0), dst0);
- _mm_storeu_si128((__m128i*)(dst+4), dst4);
- src += 8;
- dst += 8;
- count -= 8;
- }
- if (count >= 4) {
- _mm_storeu_si128((__m128i*)dst, kernel(_mm_loadu_si128((const __m128i*)src)));
- src += 4;
- dst += 4;
- count -= 4;
- }
- if (count >= 2) {
- _mm_storel_epi64((__m128i*)dst, kernel(_mm_loadl_epi64((const __m128i*)src)));
- src += 2;
- dst += 2;
- count -= 2;
- }
- if (count >= 1) {
- *dst = _mm_cvtsi128_si32(kernel(_mm_cvtsi32_si128(*src)));
- }
-#else // Neither NEON nor SSE2.
- unsigned round = (128 << 16) + (128 << 0);
-
- while (count --> 0) {
- // Our math is 16-bit, so we can do a little bit of SIMD in 32-bit registers.
- const uint32_t mask = 0x00FF00FF;
- uint32_t rb = (((*src >> 0) & mask) * invA + round) >> 8, // _r_b
- ag = (((*src >> 8) & mask) * invA + round) >> 0; // a_g_
- *dst = color + ((rb & mask) | (ag & ~mask));
- src++;
- dst++;
- }
-#endif
+ Sk4px::MapSrc(count, dst, src, [&](const Sk4px& src4) -> Sk4px {
+ return src4.mulWiden(invA_16x).addNarrowHi(colorHighAndRound);
+ });
}
-
diff --git a/src/core/SkNx.h b/src/core/SkNx.h
index 4cfc6e3..9d31962 100644
--- a/src/core/SkNx.h
+++ b/src/core/SkNx.h
@@ -30,7 +30,7 @@
bool allTrue() const { return fLo.allTrue() && fHi.allTrue(); }
bool anyTrue() const { return fLo.anyTrue() || fHi.anyTrue(); }
-private:
+protected:
REQUIRE(0 == (N & (N-1)));
SkNb<N/2, Bytes> fLo, fHi;
};
@@ -45,9 +45,12 @@
return SkNi(SkNi<N/2,T>::Load(vals), SkNi<N/2,T>::Load(vals+N/2));
}
- SkNi(T a, T b) : fLo(a), fHi(b) { REQUIRE(N==2); }
- SkNi(T a, T b, T c, T d) : fLo(a,b), fHi(c,d) { REQUIRE(N==4); }
- SkNi(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); }
+ SkNi(T a, T b) : fLo(a), fHi(b) { REQUIRE(N==2); }
+ SkNi(T a, T b, T c, T d) : fLo(a,b), fHi(c,d) { REQUIRE(N==4); }
+ SkNi(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); }
+ SkNi(T a, T b, T c, T d, T e, T f, T g, T h,
+ T i, T j, T k, T l, T m, T n, T o, T p)
+ : fLo(a,b,c,d, e,f,g,h), fHi(i,j,k,l, m,n,o,p) { REQUIRE(N==16); }
void store(T vals[N]) const {
fLo.store(vals);
@@ -68,7 +71,7 @@
return k < N/2 ? fLo.template kth<k>() : fHi.template kth<k-N/2>();
}
-private:
+protected:
REQUIRE(0 == (N & (N-1)));
SkNi<N/2, T> fLo, fHi;
@@ -133,7 +136,7 @@
return k < N/2 ? fLo.template kth<k>() : fHi.template kth<k-N/2>();
}
-private:
+protected:
REQUIRE(0 == (N & (N-1)));
SkNf(const SkNf<N/2, T>& lo, const SkNf<N/2, T>& hi) : fLo(lo), fHi(hi) {}
@@ -150,7 +153,7 @@
explicit SkNb(bool val) : fVal(val) {}
bool allTrue() const { return fVal; }
bool anyTrue() const { return fVal; }
-private:
+protected:
bool fVal;
};
@@ -175,7 +178,7 @@
return fVal;
}
-private:
+protected:
T fVal;
};
@@ -223,7 +226,7 @@
return fVal;
}
-private:
+protected:
// We do double sqrts natively, or via floats for any other type.
template <typename U>
static U Sqrt(U val) { return (U) ::sqrtf((float)val); }
@@ -263,9 +266,13 @@
typedef SkNf<4, double> Sk4d;
typedef SkNf<4, SkScalar> Sk4s;
-typedef SkNi<4, uint16_t> Sk4h;
-typedef SkNi<8, uint16_t> Sk8h;
+typedef SkNi<4, uint16_t> Sk4h;
+typedef SkNi<8, uint16_t> Sk8h;
+typedef SkNi<16, uint16_t> Sk16h;
-typedef SkNi<4, int> Sk4i;
+typedef SkNi<16, uint8_t> Sk16b;
+
+typedef SkNi<4, int32_t> Sk4i;
+typedef SkNi<4, uint32_t> Sk4u;
#endif//SkNx_DEFINED
diff --git a/src/opts/Sk4px_NEON.h b/src/opts/Sk4px_NEON.h
new file mode 100644
index 0000000..ede5f2c
--- /dev/null
+++ b/src/opts/Sk4px_NEON.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+inline Sk4px::Sk4px(SkPMColor px) : INHERITED((uint8x16_t)vdupq_n_u32(px)) {}
+
+inline Sk4px Sk4px::Load4(const SkPMColor px[4]) {
+ return Sk16b((uint8x16_t)vld1q_u32(px));
+}
+inline Sk4px Sk4px::Load2(const SkPMColor px[2]) {
+ uint32x2_t px2 = vld1_u32(px);
+ return Sk16b((uint8x16_t)vcombine_u32(px2, px2));
+}
+inline Sk4px Sk4px::Load1(const SkPMColor px[1]) {
+ return Sk16b((uint8x16_t)vdupq_n_u32(*px));
+}
+
+inline void Sk4px::store4(SkPMColor px[4]) const {
+ vst1q_u32(px, (uint32x4_t)this->fVec);
+}
+inline void Sk4px::store2(SkPMColor px[2]) const {
+ vst1_u32(px, (uint32x2_t)vget_low_u8(this->fVec));
+}
+inline void Sk4px::store1(SkPMColor px[1]) const {
+ vst1q_lane_u32(px, (uint32x4_t)this->fVec, 0);
+}
+
+inline Sk4px::Wide Sk4px::widenLo() const {
+ return Sk16h(vmovl_u8(vget_low_u8 (this->fVec)),
+ vmovl_u8(vget_high_u8(this->fVec)));
+}
+
+inline Sk4px::Wide Sk4px::widenHi() const {
+ return Sk16h(vshll_n_u8(vget_low_u8 (this->fVec), 8),
+ vshll_n_u8(vget_high_u8(this->fVec), 8));
+}
+
+inline Sk4px::Wide Sk4px::mulWiden(const Sk16b& other) const {
+ return Sk16h(vmull_u8(vget_low_u8 (this->fVec), vget_low_u8 (other.fVec)),
+ vmull_u8(vget_high_u8(this->fVec), vget_high_u8(other.fVec)));
+}
+
+inline Sk4px Sk4px::Wide::addNarrowHi(const Sk16h& other) const {
+ const Sk4px::Wide o(other); // Should be no code, but allows us to access fLo, fHi.
+ return Sk16b(vcombine_u8(vaddhn_u16(this->fLo.fVec, o.fLo.fVec),
+ vaddhn_u16(this->fHi.fVec, o.fHi.fVec)));
+}
diff --git a/src/opts/Sk4px_SSE2.h b/src/opts/Sk4px_SSE2.h
new file mode 100644
index 0000000..d036328
--- /dev/null
+++ b/src/opts/Sk4px_SSE2.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+inline Sk4px::Sk4px(SkPMColor px) : INHERITED(_mm_set1_epi32(px)) {}
+
+inline Sk4px Sk4px::Load4(const SkPMColor px[4]) {
+ return Sk16b(_mm_loadu_si128((const __m128i*)px));
+}
+inline Sk4px Sk4px::Load2(const SkPMColor px[2]) {
+ return Sk16b(_mm_loadl_epi64((const __m128i*)px));
+}
+inline Sk4px Sk4px::Load1(const SkPMColor px[1]) { return Sk16b(_mm_cvtsi32_si128(*px)); }
+
+inline void Sk4px::store4(SkPMColor px[4]) const { _mm_storeu_si128((__m128i*)px, this->fVec); }
+inline void Sk4px::store2(SkPMColor px[2]) const { _mm_storel_epi64((__m128i*)px, this->fVec); }
+inline void Sk4px::store1(SkPMColor px[1]) const { *px = _mm_cvtsi128_si32(this->fVec); }
+
+inline Sk4px::Wide Sk4px::widenLo() const {
+ return Sk16h(_mm_unpacklo_epi8(this->fVec, _mm_setzero_si128()),
+ _mm_unpackhi_epi8(this->fVec, _mm_setzero_si128()));
+}
+
+inline Sk4px::Wide Sk4px::widenHi() const {
+ return Sk16h(_mm_unpacklo_epi8(_mm_setzero_si128(), this->fVec),
+ _mm_unpackhi_epi8(_mm_setzero_si128(), this->fVec));
+}
+
+inline Sk4px::Wide Sk4px::mulWiden(const Sk16b& other) const {
+ return this->widenLo() * Sk4px(other).widenLo();
+}
+
+inline Sk4px Sk4px::Wide::addNarrowHi(const Sk16h& other) const {
+ Sk4px::Wide r = (*this + other) >> 8;
+ return Sk4px(_mm_packus_epi16(r.fLo.fVec, r.fHi.fVec));
+}
diff --git a/src/opts/Sk4px_none.h b/src/opts/Sk4px_none.h
new file mode 100644
index 0000000..c8c33a0
--- /dev/null
+++ b/src/opts/Sk4px_none.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkUtils.h"
+
+static_assert(sizeof(Sk4px) == 16, "This file uses memcpy / sk_memset32, so exact size matters.");
+
+inline Sk4px::Sk4px(SkPMColor px) {
+ sk_memset32((uint32_t*)this, px, 4);
+}
+
+inline Sk4px Sk4px::Load4(const SkPMColor px[4]) {
+ Sk4px px4 = Sk16b();
+ memcpy(&px4, px, 16);
+ return px4;
+}
+
+inline Sk4px Sk4px::Load2(const SkPMColor px[2]) {
+ Sk4px px2 = Sk16b();
+ memcpy(&px2, px, 8);
+ return px2;
+}
+
+inline Sk4px Sk4px::Load1(const SkPMColor px[1]) {
+ Sk4px px1 = Sk16b();
+ memcpy(&px1, px, 4);
+ return px1;
+}
+
+inline void Sk4px::store4(SkPMColor px[4]) const { memcpy(px, this, 16); }
+inline void Sk4px::store2(SkPMColor px[2]) const { memcpy(px, this, 8); }
+inline void Sk4px::store1(SkPMColor px[1]) const { memcpy(px, this, 4); }
+
+inline Sk4px::Wide Sk4px::widenLo() const {
+ return Sk16h(this->kth< 0>(), this->kth< 1>(), this->kth< 2>(), this->kth< 3>(),
+ this->kth< 4>(), this->kth< 5>(), this->kth< 6>(), this->kth< 7>(),
+ this->kth< 8>(), this->kth< 9>(), this->kth<10>(), this->kth<11>(),
+ this->kth<12>(), this->kth<13>(), this->kth<14>(), this->kth<15>());
+}
+
+inline Sk4px::Wide Sk4px::widenHi() const { return this->widenLo() << 8; }
+
+inline Sk4px::Wide Sk4px::mulWiden(const Sk16b& other) const {
+ return this->widenLo() * Sk4px(other).widenLo();
+}
+
+inline Sk4px Sk4px::Wide::addNarrowHi(const Sk16h& other) const {
+ Sk4px::Wide r = (*this + other) >> 8;
+ return Sk16b(r.kth< 0>(), r.kth< 1>(), r.kth< 2>(), r.kth< 3>(),
+ r.kth< 4>(), r.kth< 5>(), r.kth< 6>(), r.kth< 7>(),
+ r.kth< 8>(), r.kth< 9>(), r.kth<10>(), r.kth<11>(),
+ r.kth<12>(), r.kth<13>(), r.kth<14>(), r.kth<15>());
+}
diff --git a/src/opts/SkNx_neon.h b/src/opts/SkNx_neon.h
index f1deabc..b9d4357 100644
--- a/src/opts/SkNx_neon.h
+++ b/src/opts/SkNx_neon.h
@@ -10,6 +10,28 @@
#include <arm_neon.h>
+// Well, this is absurd. The shifts require compile-time constant arguments.
+
+#define SHIFT8(op, v, bits) switch(bits) { \
+ case 1: return op(v, 1); case 2: return op(v, 2); case 3: return op(v, 3); \
+ case 4: return op(v, 4); case 5: return op(v, 5); case 6: return op(v, 6); \
+ case 7: return op(v, 7); \
+ } return fVec
+
+#define SHIFT16(op, v, bits) if (bits < 8) { SHIFT8(op, v, bits); } switch(bits) { \
+ case 8: return op(v, 8); case 9: return op(v, 9); \
+ case 10: return op(v, 10); case 11: return op(v, 11); case 12: return op(v, 12); \
+ case 13: return op(v, 13); case 14: return op(v, 14); case 15: return op(v, 15); \
+ } return fVec
+
+#define SHIFT32(op, v, bits) if (bits < 16) { SHIFT16(op, v, bits); } switch(bits) { \
+ case 16: return op(v, 16); case 17: return op(v, 17); case 18: return op(v, 18); \
+ case 19: return op(v, 19); case 20: return op(v, 20); case 21: return op(v, 21); \
+ case 22: return op(v, 22); case 23: return op(v, 23); case 24: return op(v, 24); \
+ case 25: return op(v, 25); case 26: return op(v, 26); case 27: return op(v, 27); \
+ case 28: return op(v, 28); case 29: return op(v, 29); case 30: return op(v, 30); \
+ case 31: return op(v, 31); } return fVec
+
template <>
class SkNb<2, 4> {
public:
@@ -18,7 +40,7 @@
SkNb() {}
bool allTrue() const { return vget_lane_u32(fVec, 0) && vget_lane_u32(fVec, 1); }
bool anyTrue() const { return vget_lane_u32(fVec, 0) || vget_lane_u32(fVec, 1); }
-private:
+
uint32x2_t fVec;
};
@@ -32,7 +54,7 @@
&& vgetq_lane_u32(fVec, 2) && vgetq_lane_u32(fVec, 3); }
bool anyTrue() const { return vgetq_lane_u32(fVec, 0) || vgetq_lane_u32(fVec, 1)
|| vgetq_lane_u32(fVec, 2) || vgetq_lane_u32(fVec, 3); }
-private:
+
uint32x4_t fVec;
};
@@ -104,7 +126,6 @@
return vget_lane_f32(fVec, k&1);
}
-private:
float32x2_t fVec;
};
@@ -117,7 +138,7 @@
SkNb() {}
bool allTrue() const { return vgetq_lane_u64(fVec, 0) && vgetq_lane_u64(fVec, 1); }
bool anyTrue() const { return vgetq_lane_u64(fVec, 0) || vgetq_lane_u64(fVec, 1); }
-private:
+
uint64x2_t fVec;
};
@@ -181,7 +202,6 @@
return vgetq_lane_f64(fVec, k&1);
}
-private:
float64x2_t fVec;
};
#endif//defined(SK_CPU_ARM64)
@@ -202,29 +222,14 @@
SkNi operator - (const SkNi& o) const { return vsubq_s32(fVec, o.fVec); }
SkNi operator * (const SkNi& o) const { return vmulq_s32(fVec, o.fVec); }
- // Well, this is absurd. The shifts require compile-time constant arguments.
-#define SHIFT(op, v, bits) switch(bits) { \
- case 1: return op(v, 1); case 2: return op(v, 2); case 3: return op(v, 3); \
- case 4: return op(v, 4); case 5: return op(v, 5); case 6: return op(v, 6); \
- case 7: return op(v, 7); case 8: return op(v, 8); case 9: return op(v, 9); \
- case 10: return op(v, 10); case 11: return op(v, 11); case 12: return op(v, 12); \
- case 13: return op(v, 13); case 14: return op(v, 14); case 15: return op(v, 15); \
- case 16: return op(v, 16); case 17: return op(v, 17); case 18: return op(v, 18); \
- case 19: return op(v, 19); case 20: return op(v, 20); case 21: return op(v, 21); \
- case 22: return op(v, 22); case 23: return op(v, 23); case 24: return op(v, 24); \
- case 25: return op(v, 25); case 26: return op(v, 26); case 27: return op(v, 27); \
- case 28: return op(v, 28); case 29: return op(v, 29); case 30: return op(v, 30); \
- case 31: return op(v, 31); } return fVec
-
- SkNi operator << (int bits) const { SHIFT(vshlq_n_s32, fVec, bits); }
- SkNi operator >> (int bits) const { SHIFT(vshrq_n_s32, fVec, bits); }
-#undef SHIFT
+ SkNi operator << (int bits) const { SHIFT32(vshlq_n_s32, fVec, bits); }
+ SkNi operator >> (int bits) const { SHIFT32(vshrq_n_s32, fVec, bits); }
template <int k> int kth() const {
SkASSERT(0 <= k && k < 4);
return vgetq_lane_s32(fVec, k&3);
}
-protected:
+
int32x4_t fVec;
};
@@ -298,8 +303,75 @@
return vgetq_lane_f32(fVec, k&3);
}
-protected:
float32x4_t fVec;
};
+template <>
+class SkNi<8, uint16_t> {
+public:
+ SkNi(const uint16x8_t& vec) : fVec(vec) {}
+
+ SkNi() {}
+ explicit SkNi(uint16_t val) : fVec(vdupq_n_u16(val)) {}
+ static SkNi Load(const uint16_t vals[8]) { return vld1q_u16(vals); }
+
+ SkNi(uint16_t a, uint16_t b, uint16_t c, uint16_t d,
+ uint16_t e, uint16_t f, uint16_t g, uint16_t h) {
+ fVec = (uint16x8_t) { a,b,c,d, e,f,g,h };
+ }
+
+ void store(uint16_t vals[8]) const { vst1q_u16(vals, fVec); }
+
+ SkNi operator + (const SkNi& o) const { return vaddq_u16(fVec, o.fVec); }
+ SkNi operator - (const SkNi& o) const { return vsubq_u16(fVec, o.fVec); }
+ SkNi operator * (const SkNi& o) const { return vmulq_u16(fVec, o.fVec); }
+
+ SkNi operator << (int bits) const { SHIFT16(vshlq_n_u16, fVec, bits); }
+ SkNi operator >> (int bits) const { SHIFT16(vshrq_n_u16, fVec, bits); }
+
+ template <int k> uint16_t kth() const {
+ SkASSERT(0 <= k && k < 8);
+ return vgetq_lane_u16(fVec, k&7);
+ }
+
+ uint16x8_t fVec;
+};
+
+template <>
+class SkNi<16, uint8_t> {
+public:
+ SkNi(const uint8x16_t& vec) : fVec(vec) {}
+
+ SkNi() {}
+ explicit SkNi(uint8_t val) : fVec(vdupq_n_u8(val)) {}
+ static SkNi Load(const uint8_t vals[16]) { return vld1q_u8(vals); }
+
+ SkNi(uint8_t a, uint8_t b, uint8_t c, uint8_t d,
+ uint8_t e, uint8_t f, uint8_t g, uint8_t h,
+ uint8_t i, uint8_t j, uint8_t k, uint8_t l,
+ uint8_t m, uint8_t n, uint8_t o, uint8_t p) {
+ fVec = (uint8x16_t) { a,b,c,d, e,f,g,h, i,j,k,l, m,n,o,p };
+ }
+
+ void store(uint8_t vals[16]) const { vst1q_u8(vals, fVec); }
+
+ SkNi operator + (const SkNi& o) const { return vaddq_u8(fVec, o.fVec); }
+ SkNi operator - (const SkNi& o) const { return vsubq_u8(fVec, o.fVec); }
+ SkNi operator * (const SkNi& o) const { return vmulq_u8(fVec, o.fVec); }
+
+ SkNi operator << (int bits) const { SHIFT8(vshlq_n_u8, fVec, bits); }
+ SkNi operator >> (int bits) const { SHIFT8(vshrq_n_u8, fVec, bits); }
+
+ template <int k> uint8_t kth() const {
+ SkASSERT(0 <= k && k < 15);
+ return vgetq_lane_u8(fVec, k&16);
+ }
+
+ uint8x16_t fVec;
+};
+
+#undef SHIFT32
+#undef SHIFT16
+#undef SHIFT8
+
#endif//SkNx_neon_DEFINED
diff --git a/src/opts/SkNx_sse.h b/src/opts/SkNx_sse.h
index cbe624b..b3339f9 100644
--- a/src/opts/SkNx_sse.h
+++ b/src/opts/SkNx_sse.h
@@ -20,7 +20,6 @@
bool allTrue() const { return 0xff == (_mm_movemask_epi8(fVec) & 0xff); }
bool anyTrue() const { return 0x00 != (_mm_movemask_epi8(fVec) & 0xff); }
-private:
__m128i fVec;
};
@@ -33,7 +32,6 @@
bool allTrue() const { return 0xffff == _mm_movemask_epi8(fVec); }
bool anyTrue() const { return 0x0000 != _mm_movemask_epi8(fVec); }
-private:
__m128i fVec;
};
@@ -46,7 +44,6 @@
bool allTrue() const { return 0xffff == _mm_movemask_epi8(fVec); }
bool anyTrue() const { return 0x0000 != _mm_movemask_epi8(fVec); }
-private:
__m128i fVec;
};
@@ -95,7 +92,6 @@
return pun.fs[k&1];
}
-private:
__m128 fVec;
};
@@ -141,7 +137,6 @@
return pun.ds[k&1];
}
-private:
__m128d fVec;
};
@@ -179,7 +174,7 @@
default: SkASSERT(false); return 0;
}
}
-protected:
+
__m128i fVec;
};
@@ -227,7 +222,6 @@
return pun.fs[k&3];
}
-protected:
__m128 fVec;
};
@@ -254,7 +248,7 @@
SkASSERT(0 <= k && k < 4);
return _mm_extract_epi16(fVec, k);
}
-protected:
+
__m128i fVec;
};
@@ -282,7 +276,41 @@
SkASSERT(0 <= k && k < 8);
return _mm_extract_epi16(fVec, k);
}
-protected:
+
+ __m128i fVec;
+};
+
+template <>
+class SkNi<16, uint8_t> {
+public:
+ SkNi(const __m128i& vec) : fVec(vec) {}
+
+ SkNi() {}
+ explicit SkNi(uint8_t val) : fVec(_mm_set1_epi8(val)) {}
+ static SkNi Load(const uint8_t vals[16]) { return _mm_loadu_si128((const __m128i*)vals); }
+ SkNi(uint8_t a, uint8_t b, uint8_t c, uint8_t d,
+ uint8_t e, uint8_t f, uint8_t g, uint8_t h,
+ uint8_t i, uint8_t j, uint8_t k, uint8_t l,
+ uint8_t m, uint8_t n, uint8_t o, uint8_t p)
+ : fVec(_mm_setr_epi8(a,b,c,d, e,f,g,h, i,j,k,l, m,n,o,p)) {}
+
+ void store(uint8_t vals[16]) const { _mm_storeu_si128((__m128i*)vals, fVec); }
+
+ SkNi operator + (const SkNi& o) const { return _mm_add_epi8(fVec, o.fVec); }
+ SkNi operator - (const SkNi& o) const { return _mm_sub_epi8(fVec, o.fVec); }
+
+ // SSE cannot multiply or shift vectors of uint8_t.
+ SkNi operator * (const SkNi& o) const { SkASSERT(false); return fVec; }
+ SkNi operator << (int bits) const { SkASSERT(false); return fVec; }
+ SkNi operator >> (int bits) const { SkASSERT(false); return fVec; }
+
+ template <int k> uint8_t kth() const {
+ SkASSERT(0 <= k && k < 16);
+ // SSE4.1 would just `return _mm_extract_epi8(fVec, k)`. We have to read 16-bits instead.
+ int pair = _mm_extract_epi16(fVec, k/2);
+ return k % 2 == 0 ? pair : (pair >> 8);
+ }
+
__m128i fVec;
};