diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-05-04 14:46:35 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-05-27 00:18:04 +0200 |
commit | f3e7073f938ead15d8651623a23fe402186cb709 (patch) | |
tree | 5c5d51cfecb4ed105560cfe0b6e8946f64694ec7 /src/gui/painting | |
parent | 1a86bfa672afcb140e87e7cc8d60899b29a847f7 (diff) |
Add NEON optimizations to QColorTransform
And avoid some needless shuffling.
Change-Id: I5101eb0582c17f5e3cb6db8e20d315ef38bd1863
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Diffstat (limited to 'src/gui/painting')
-rw-r--r-- | src/gui/painting/qcolortransform.cpp | 133 |
1 files changed, 125 insertions, 8 deletions
diff --git a/src/gui/painting/qcolortransform.cpp b/src/gui/painting/qcolortransform.cpp index 2a1b0ede20..116a6c0ec7 100644 --- a/src/gui/painting/qcolortransform.cpp +++ b/src/gui/painting/qcolortransform.cpp @@ -271,6 +271,24 @@ static void applyMatrix(QColorVector *buffer, const qsizetype len, const QColorM cx = _mm_max_ps(cx, minV); _mm_storeu_ps(&buffer[j].x, cx); } +#elif defined(__ARM_NEON__) + const float32x4_t minV = vdupq_n_f32(0.0f); + const float32x4_t maxV = vdupq_n_f32(1.0f); + const float32x4_t xMat = vld1q_f32(&colorMatrix.r.x); + const float32x4_t yMat = vld1q_f32(&colorMatrix.g.x); + const float32x4_t zMat = vld1q_f32(&colorMatrix.b.x); + for (qsizetype j = 0; j < len; ++j) { + float32x4_t c = vld1q_f32(&buffer[j].x); + float32x4_t cx = vmulq_n_f32(xMat, vgetq_lane_f32(c, 0)); + float32x4_t cy = vmulq_n_f32(yMat, vgetq_lane_f32(c, 1)); + float32x4_t cz = vmulq_n_f32(zMat, vgetq_lane_f32(c, 2)); + cx = vaddq_f32(cx, cy); + cx = vaddq_f32(cx, cz); + // Clamp: + cx = vminq_f32(cx, maxV); + cx = vmaxq_f32(cx, minV); + vst1q_f32(&buffer[j].x, cx); + } #else for (int j = 0; j < len; ++j) { const QColorVector cv = colorMatrix.map(buffer[j]); @@ -282,6 +300,13 @@ static void applyMatrix(QColorVector *buffer, const qsizetype len, const QColorM } template<typename T> +static constexpr inline bool isArgb(); +template<> +constexpr inline bool isArgb<QRgb>() { return true; } +template<> +constexpr inline bool isArgb<QRgba64>() { return false; } + +template<typename T> static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr); template<typename T> static void loadUnpremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr); @@ -312,8 +337,6 @@ inline void loadP<QRgba64>(const QRgba64 &p, __m128i &v) #else v = _mm_unpacklo_epi16(v, _mm_setzero_si128()); #endif - // Shuffle to ARGB as the template below expects it - v = _mm_shuffle_epi32(v, _MM_SHUFFLE(3, 0, 1, 2)); } template<typename T> @@ -321,6 +344,7 @@ static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetyp { const __m128 v4080 = _mm_set1_ps(4080.f); const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256)); + constexpr bool isARGB = isArgb<T>(); for (qsizetype i = 0; i < len; ++i) { __m128i v; loadP<T>(src[i], v); @@ -338,9 +362,9 @@ static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetyp // LUT v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080)); - const int ridx = _mm_extract_epi16(v, 4); + const int ridx = isARGB ? _mm_extract_epi16(v, 4) : _mm_extract_epi16(v, 0); const int gidx = _mm_extract_epi16(v, 2); - const int bidx = _mm_extract_epi16(v, 0); + const int bidx = isARGB ? _mm_extract_epi16(v, 0) : _mm_extract_epi16(v, 4); v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx], 0); v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx], 2); v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx], 4); @@ -378,20 +402,19 @@ inline void loadPU<QRgba64>(const QRgba64 &p, __m128i &v) v = _mm_unpacklo_epi16(v, _mm_setzero_si128()); #endif v = _mm_srli_epi32(v, 4); - // Shuffle to ARGB as the template below expects it - v = _mm_shuffle_epi32(v, _MM_SHUFFLE(3, 0, 1, 2)); } template<typename T> void loadUnpremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr) { + constexpr bool isARGB = isArgb<T>(); const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256)); for (qsizetype i = 0; i < len; ++i) { __m128i v; loadPU<T>(src[i], v); - const int ridx = _mm_extract_epi16(v, 4); + const int ridx = isARGB ? _mm_extract_epi16(v, 4) : _mm_extract_epi16(v, 0); const int gidx = _mm_extract_epi16(v, 2); - const int bidx = _mm_extract_epi16(v, 0); + const int bidx = isARGB ? _mm_extract_epi16(v, 0) : _mm_extract_epi16(v, 4); v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx], 0); v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx], 2); v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx], 4); @@ -400,6 +423,100 @@ void loadUnpremultiplied(QColorVector *buffer, const T *src, const qsizetype len } } +#elif defined(__ARM_NEON__) +// Load to [0-alpha] in 4x32 SIMD +template<typename T> +static inline void loadP(const T &p, uint32x4_t &v); + +template<> +inline void loadP<QRgb>(const QRgb &p, uint32x4_t &v) +{ + v = vmovl_u16(vget_low_u16(vmovl_u8(vreinterpret_u8_u32(vmov_n_u32(p))))); +} + +template<> +inline void loadP<QRgba64>(const QRgba64 &p, uint32x4_t &v) +{ + v = vmovl_u16(vreinterpret_u16_u64(vld1_u64(reinterpret_cast<const uint64_t *>(&p)))); +} + +template<typename T> +static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr) +{ + constexpr bool isARGB = isArgb<T>(); + const float iFF00 = 1.0f / (255 * 256); + for (qsizetype i = 0; i < len; ++i) { + uint32x4_t v; + loadP<T>(src[i], v); + float32x4_t vf = vcvtq_f32_u32(v); + // Approximate 1/a: + float32x4_t va = vdupq_n_f32(vgetq_lane_f32(vf, 3)); + float32x4_t via = vrecpeq_f32(va); // estimate 1/a + via = vmulq_f32(vrecpsq_f32(va, via), via); + + // v * (1/a) + vf = vmulq_f32(vf, via); + + // Handle zero alpha +#if defined(Q_PROCESSOR_ARM_64) + uint32x4_t vAlphaMask = vceqzq_f32(va); +#else + uint32x4_t vAlphaMask = vceqq_f32(va, vdupq_n_f32(0.0)); +#endif + vf = vreinterpretq_f32_u32(vbicq_u32(vreinterpretq_u32_f32(vf), vAlphaMask)); + + // LUT + v = vcvtq_u32_f32(vmulq_n_f32(vf, 4080.f)); + const int ridx = isARGB ? vgetq_lane_u32(v, 2) : vgetq_lane_u32(v, 0); + const int gidx = vgetq_lane_u32(v, 1); + const int bidx = isARGB ? vgetq_lane_u32(v, 0) : vgetq_lane_u32(v, 2); + v = vsetq_lane_u32(d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx], v, 0); + v = vsetq_lane_u32(d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx], v, 1); + v = vsetq_lane_u32(d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx], v, 2); + vf = vmulq_n_f32(vcvtq_f32_u32(v), iFF00); + + vst1q_f32(&buffer[i].x, vf); + } +} + +// Load to [0-4080] in 4x32 SIMD +template<typename T> +static inline void loadPU(const T &p, uint32x4_t &v); + +template<> +inline void loadPU<QRgb>(const QRgb &p, uint32x4_t &v) +{ + v = vmovl_u16(vget_low_u16(vmovl_u8(vreinterpret_u8_u32(vmov_n_u32(p))))); + v = vshlq_n_u32(v, 4); +} + +template<> +inline void loadPU<QRgba64>(const QRgba64 &p, uint32x4_t &v) +{ + uint16x4_t v16 = vreinterpret_u16_u64(vld1_u64(reinterpret_cast<const uint64_t *>(&p))); + v16 = vsub_u16(v16, vshr_n_u16(v16, 8)); + v = vmovl_u16(v16); + v = vshrq_n_u32(v, 4); +} + +template<typename T> +void loadUnpremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr) +{ + constexpr bool isARGB = isArgb<T>(); + const float iFF00 = 1.0f / (255 * 256); + for (qsizetype i = 0; i < len; ++i) { + uint32x4_t v; + loadPU<T>(src[i], v); + const int ridx = isARGB ? vgetq_lane_u32(v, 2) : vgetq_lane_u32(v, 0); + const int gidx = vgetq_lane_u32(v, 1); + const int bidx = isARGB ? vgetq_lane_u32(v, 0) : vgetq_lane_u32(v, 2); + v = vsetq_lane_u32(d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx], v, 0); + v = vsetq_lane_u32(d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx], v, 1); + v = vsetq_lane_u32(d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx], v, 2); + float32x4_t vf = vmulq_n_f32(vcvtq_f32_u32(v), iFF00); + vst1q_f32(&buffer[i].x, vf); + } +} #else template<> void loadPremultiplied<QRgb>(QColorVector *buffer, const QRgb *src, const qsizetype len, const QColorTransformPrivate *d_ptr) |