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
This commit is contained in:
mtklein 2015-05-12 06:11:21 -07:00 committed by Commit bot
parent 06f3647cad
commit d2ffd36eb6
8 changed files with 397 additions and 134 deletions

92
src/core/Sk4px.h Normal file
View File

@ -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

View File

@ -131,6 +131,8 @@ SkBlitRow::Proc32 SkBlitRow::Factory32(unsigned flags) {
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 @@ void SkBlitRow::Color32(SkPMColor dst[], const SkPMColor src[], int count, SkPMC
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);
});
}

View File

@ -30,7 +30,7 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
explicit SkNb(bool val) : fVal(val) {}
bool allTrue() const { return fVal; }
bool anyTrue() const { return fVal; }
private:
protected:
bool fVal;
};
@ -175,7 +178,7 @@ public:
return fVal;
}
private:
protected:
T fVal;
};
@ -223,7 +226,7 @@ public:
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, float> Sk4f;
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

50
src/opts/Sk4px_NEON.h Normal file
View File

@ -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)));
}

39
src/opts/Sk4px_SSE2.h Normal file
View File

@ -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));
}

57
src/opts/Sk4px_none.h Normal file
View File

@ -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>());
}

View File

@ -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 @@ public:
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 @@ public:
&& 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 @@ public:
return vget_lane_f32(fVec, k&1);
}
private:
float32x2_t fVec;
};
@ -117,7 +138,7 @@ public:
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 @@ public:
return vgetq_lane_f64(fVec, k&1);
}
private:
float64x2_t fVec;
};
#endif//defined(SK_CPU_ARM64)
@ -202,29 +222,14 @@ public:
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 @@ public:
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

View File

@ -20,7 +20,6 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
return pun.fs[k&1];
}
private:
__m128 fVec;
};
@ -141,7 +137,6 @@ public:
return pun.ds[k&1];
}
private:
__m128d fVec;
};
@ -179,7 +174,7 @@ public:
default: SkASSERT(false); return 0;
}
}
protected:
__m128i fVec;
};
@ -227,7 +222,6 @@ public:
return pun.fs[k&3];
}
protected:
__m128 fVec;
};
@ -254,7 +248,7 @@ public:
SkASSERT(0 <= k && k < 4);
return _mm_extract_epi16(fVec, k);
}
protected:
__m128i fVec;
};
@ -282,7 +276,41 @@ public:
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;
};