summaryrefslogtreecommitdiffstats
path: root/src/gui/painting/qdrawhelper_p.h
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@theqtcompany.com>2015-12-14 12:49:36 +0100
committerTor Arne Vestbø <tor.arne.vestbo@theqtcompany.com>2015-12-17 01:11:57 +0000
commitd290424f2aad53eec5a94909703d988009fde973 (patch)
tree934d4828404efd8d0e3d6a1a288caac2ee974fda /src/gui/painting/qdrawhelper_p.h
parent3e892e4a972446d17afde178a7b17482ecaced33 (diff)
NEON optimized bilinear sampling
Adds NEON version of interpolate_4_pixels used by smooth upscaling, and bilinear sampling. The SSE2 version is reordered to match the NEON version so they have the same order of operations and a faster version that loads directly into vector registers. Testing is extended so we have a test of smoothness that can catch more possible mistakes. Change-Id: I0de4aecf5cb79468e7c8f19f421aa24b2955547c Reviewed-by: Erik Verbruggen <erik.verbruggen@theqtcompany.com>
Diffstat (limited to 'src/gui/painting/qdrawhelper_p.h')
-rw-r--r--src/gui/painting/qdrawhelper_p.h95
1 files changed, 72 insertions, 23 deletions
diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h
index 1ff19f4e04..fc24e22cac 100644
--- a/src/gui/painting/qdrawhelper_p.h
+++ b/src/gui/painting/qdrawhelper_p.h
@@ -629,31 +629,75 @@ static Q_ALWAYS_INLINE uint BYTE_MUL(uint x, uint a) {
}
#endif
-#ifdef __SSE2__
+#if defined(__SSE2__)
+static Q_ALWAYS_INLINE uint interpolate_4_pixels_sse2(__m128i vt, __m128i vb, uint distx, uint disty)
+{
+ // First interpolate top and bottom pixels in parallel.
+ vt = _mm_unpacklo_epi8(vt, _mm_setzero_si128());
+ vb = _mm_unpacklo_epi8(vb, _mm_setzero_si128());
+ vt = _mm_mullo_epi16(vt, _mm_set1_epi16(256 - disty));
+ vb = _mm_mullo_epi16(vb, _mm_set1_epi16(disty));
+ __m128i vlr = _mm_add_epi16(vt, vb);
+ vlr = _mm_srli_epi16(vlr, 8);
+ // vlr now contains the result of the first two interpolate calls vlr = unpacked((xright << 64) | xleft)
+
+ // Now the last interpolate between left and right..
+ const __m128i vidistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(256 - distx), _MM_SHUFFLE(0, 0, 0, 0));
+ const __m128i vdistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(distx), _MM_SHUFFLE(0, 0, 0, 0));
+ const __m128i vmulx = _mm_unpacklo_epi16(vidistx, vdistx);
+ vlr = _mm_unpacklo_epi16(vlr, _mm_srli_si128(vlr, 8));
+ // vlr now contains the colors of left and right interleaved { la, ra, lr, rr, lg, rg, lb, rb }
+ vlr = _mm_madd_epi16(vlr, vmulx); // Multiply and horizontal add.
+ vlr = _mm_srli_epi32(vlr, 8);
+ vlr = _mm_packs_epi32(vlr, vlr);
+ vlr = _mm_packus_epi16(vlr, vlr);
+ return _mm_cvtsi128_si32(vlr);
+}
+
+static inline uint interpolate_4_pixels(uint tl, uint tr, uint bl, uint br, uint distx, uint disty)
+{
+ __m128i vt = _mm_unpacklo_epi32(_mm_cvtsi32_si128(tl), _mm_cvtsi32_si128(tr));
+ __m128i vb = _mm_unpacklo_epi32(_mm_cvtsi32_si128(bl), _mm_cvtsi32_si128(br));
+ return interpolate_4_pixels_sse2(vt, vb, distx, disty);
+}
+
+static inline uint interpolate_4_pixels(const uint t[], const uint b[], uint distx, uint disty)
+{
+ __m128i vt = _mm_loadl_epi64((const __m128i*)t);
+ __m128i vb = _mm_loadl_epi64((const __m128i*)b);
+ return interpolate_4_pixels_sse2(vt, vb, distx, disty);
+}
+#elif defined(__ARM_NEON__)
+static Q_ALWAYS_INLINE uint interpolate_4_pixels_neon(uint32x2_t vt32, uint32x2_t vb32, uint distx, uint disty)
+{
+ uint16x8_t vt16 = vmovl_u8(vreinterpret_u8_u32(vt32));
+ uint16x8_t vb16 = vmovl_u8(vreinterpret_u8_u32(vb32));
+ vt16 = vmulq_n_u16(vt16, 256 - disty);
+ vt16 = vmlaq_n_u16(vt16, vb16, disty);
+ vt16 = vshrq_n_u16(vt16, 8);
+ uint16x4_t vl16 = vget_low_u16(vt16);
+ uint16x4_t vr16 = vget_high_u16(vt16);
+ vl16 = vmul_n_u16(vl16, 256 - distx);
+ vl16 = vmla_n_u16(vl16, vr16, distx);
+ vl16 = vshr_n_u16(vl16, 8);
+ uint8x8_t vr = vmovn_u16(vcombine_u16(vl16, vl16));
+ return vget_lane_u32(vreinterpret_u32_u8(vr), 0);
+}
+
static inline uint interpolate_4_pixels(uint tl, uint tr, uint bl, uint br, uint distx, uint disty)
{
- // First interpolate right and left pixels in parallel.
- __m128i vl = _mm_unpacklo_epi32(_mm_cvtsi32_si128(tl), _mm_cvtsi32_si128(bl));
- __m128i vr = _mm_unpacklo_epi32(_mm_cvtsi32_si128(tr), _mm_cvtsi32_si128(br));
- vl = _mm_unpacklo_epi8(vl, _mm_setzero_si128());
- vr = _mm_unpacklo_epi8(vr, _mm_setzero_si128());
- vl = _mm_mullo_epi16(vl, _mm_set1_epi16(256 - distx));
- vr = _mm_mullo_epi16(vr, _mm_set1_epi16(distx));
- __m128i vtb = _mm_add_epi16(vl, vr);
- vtb = _mm_srli_epi16(vtb, 8);
- // vtb now contains the result of the first two interpolate calls vtb = unpacked((xbot << 64) | xtop)
-
- // Now the last interpolate between top and bottom interpolations.
- const __m128i vidisty = _mm_shufflelo_epi16(_mm_cvtsi32_si128(256 - disty), _MM_SHUFFLE(0, 0, 0, 0));
- const __m128i vdisty = _mm_shufflelo_epi16(_mm_cvtsi32_si128(disty), _MM_SHUFFLE(0, 0, 0, 0));
- const __m128i vmuly = _mm_unpacklo_epi16(vidisty, vdisty);
- vtb = _mm_unpacklo_epi16(vtb, _mm_srli_si128(vtb, 8));
- // vtb now contains the colors of top and bottom interleaved { ta, ba, tr, br, tg, bg, tb, bb }
- vtb = _mm_madd_epi16(vtb, vmuly); // Multiply and horizontal add.
- vtb = _mm_srli_epi32(vtb, 8);
- vtb = _mm_packs_epi32(vtb, _mm_setzero_si128());
- vtb = _mm_packus_epi16(vtb, _mm_setzero_si128());
- return _mm_cvtsi128_si32(vtb);
+ uint32x2_t vt32 = vmov_n_u32(tl);
+ uint32x2_t vb32 = vmov_n_u32(bl);
+ vt32 = vset_lane_u32(tr, vt32, 1);
+ vb32 = vset_lane_u32(br, vb32, 1);
+ return interpolate_4_pixels_neon(vt32, vb32, distx, disty);
+}
+
+static inline uint interpolate_4_pixels(const uint t[], const uint b[], uint distx, uint disty)
+{
+ uint32x2_t vt32 = vld1_u32(t);
+ uint32x2_t vb32 = vld1_u32(b);
+ return interpolate_4_pixels_neon(vt32, vb32, distx, disty);
}
#else
static inline uint interpolate_4_pixels(uint tl, uint tr, uint bl, uint br, uint distx, uint disty)
@@ -664,6 +708,11 @@ static inline uint interpolate_4_pixels(uint tl, uint tr, uint bl, uint br, uint
uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx);
return INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty);
}
+
+static inline uint interpolate_4_pixels(const uint t[], const uint b[], uint distx, uint disty)
+{
+ return interpolate_4_pixels(t[0], t[1], b[0], b[1], distx, disty);
+}
#endif
#if Q_BYTE_ORDER == Q_BIG_ENDIAN