From 4e6a42cdd050c8714686fa53b3246c4fc66d18fd Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Fri, 12 Oct 2018 16:52:54 +0200 Subject: [PATCH] Optimize ARGB32->RGBA64PM better This conversion is critical for ARGB32 painting, and no compiler optimized the premultiplication efficiently. Change-Id: Iee137c2f7020246478d09e880a7a1bf2ed3c6fd4 Reviewed-by: Thiago Macieira --- src/gui/painting/qdrawhelper.cpp | 48 ++++++++------ src/gui/painting/qdrawhelper_neon.cpp | 94 +++++++++++++++++++++++++++ src/gui/painting/qdrawhelper_sse4.cpp | 77 ++++++++++++++++++++++ 3 files changed, 199 insertions(+), 20 deletions(-) diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index af9375fcec..cdf5826672 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -1086,18 +1086,8 @@ static const QRgba64 *QT_FASTCALL fetchRGB32ToRGB64(QRgba64 *buffer, const uchar static const QRgba64 *QT_FASTCALL convertARGB32ToRGBA64PM(QRgba64 *buffer, const uint *src, int count, const QVector *, QDitherInfo *) { -#ifdef __SSE2__ - qConvertARGB32PMToRGBA64PM_sse2(buffer, src, count); - for (int i = 0; i < count; ++i) - buffer[i] = buffer[i].premultiplied(); -#elif defined(__ARM_NEON__) - qConvertARGB32PMToRGBA64PM_neon(buffer, src, count); - for (int i = 0; i < count; ++i) - buffer[i] = buffer[i].premultiplied(); -#else for (int i = 0; i < count; ++i) buffer[i] = QRgba64::fromArgb32(src[i]).premultiplied(); -#endif return buffer; } @@ -1149,18 +1139,8 @@ static const QRgba64 *QT_FASTCALL fetchRGBA64ToRGBA64PM(QRgba64 *buffer, const u static const QRgba64 *QT_FASTCALL convertRGBA8888ToRGBA64PM(QRgba64 *buffer, const uint *src, int count, const QVector *, QDitherInfo *) { -#ifdef __SSE2__ - qConvertARGB32PMToRGBA64PM_sse2(buffer, src, count); - for (int i = 0; i < count; ++i) - buffer[i] = buffer[i].premultiplied(); -#elif defined(__ARM_NEON__) - qConvertARGB32PMToRGBA64PM_neon(buffer, src, count); - for (int i = 0; i < count; ++i) - buffer[i] = buffer[i].premultiplied(); -#else for (int i = 0; i < count; ++i) buffer[i] = QRgba64::fromArgb32(RGBA2ARGB(src[i])).premultiplied(); -#endif return buffer; } @@ -6514,6 +6494,14 @@ static void qInitDrawhelperFunctions() const QVector *, QDitherInfo *); extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count, const QVector *, QDitherInfo *); + extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int count, + const QVector *, QDitherInfo *); + extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int count, + const QVector *, QDitherInfo *); + extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_sse4(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector *, QDitherInfo *); + extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_sse4(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector *, QDitherInfo *); extern void QT_FASTCALL storeARGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, const QVector *, QDitherInfo *); extern void QT_FASTCALL storeRGBA8888FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, @@ -6530,8 +6518,14 @@ static void qInitDrawhelperFunctions() qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_sse4; qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_sse4; qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_sse4; + qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_sse4; + qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_sse4; qPixelLayouts[QImage::Format_ARGB32].storeFromARGB32PM = storeARGB32FromARGB32PM_sse4; + qPixelLayouts[QImage::Format_RGBA8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_sse4; + qPixelLayouts[QImage::Format_RGBA8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_sse4; qPixelLayouts[QImage::Format_RGBA8888].storeFromARGB32PM = storeRGBA8888FromARGB32PM_sse4; + qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_sse4; + qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_sse4; qPixelLayouts[QImage::Format_RGBX8888].storeFromARGB32PM = storeRGBXFromARGB32PM_sse4; qPixelLayouts[QImage::Format_A2BGR30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4; qPixelLayouts[QImage::Format_A2RGB30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4; @@ -6620,6 +6614,14 @@ static void qInitDrawhelperFunctions() const QVector *, QDitherInfo *); extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_neon(uint *buffer, const uchar *src, int index, int count, const QVector *, QDitherInfo *); + extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count, + const QVector *, QDitherInfo *); + extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count, + const QVector *, QDitherInfo *); + extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_neon(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector *, QDitherInfo *); + extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_neon(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector *, QDitherInfo *); extern void QT_FASTCALL storeARGB32FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count, const QVector *, QDitherInfo *); extern void QT_FASTCALL storeRGBA8888FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count, @@ -6629,10 +6631,16 @@ static void qInitDrawhelperFunctions() qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_neon; qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_neon; qPixelLayouts[QImage::Format_ARGB32].storeFromARGB32PM = storeARGB32FromARGB32PM_neon; + qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_neon; + qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_neon; qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_neon; qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_neon; qPixelLayouts[QImage::Format_RGBA8888].storeFromARGB32PM = storeRGBA8888FromARGB32PM_neon; + qPixelLayouts[QImage::Format_RGBA8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_neon; + qPixelLayouts[QImage::Format_RGBA8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_neon; qPixelLayouts[QImage::Format_RGBX8888].storeFromARGB32PM = storeRGBXFromARGB32PM_neon; + qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_neon; + qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_neon; #endif #if defined(ENABLE_PIXMAN_DRAWHELPERS) diff --git a/src/gui/painting/qdrawhelper_neon.cpp b/src/gui/painting/qdrawhelper_neon.cpp index e33af3b784..3fcbcfd053 100644 --- a/src/gui/painting/qdrawhelper_neon.cpp +++ b/src/gui/painting/qdrawhelper_neon.cpp @@ -1149,6 +1149,72 @@ static inline void convertARGBToARGB32PM_neon(uint *buffer, const uint *src, int } } +template +static inline void convertARGB32ToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count) +{ + if (count <= 0) + return; + + const uint8x8_t shuffleMask = { 3, 3, 3, 3, 7, 7, 7, 7}; + const uint64x2_t blendMask = vdupq_n_u64(Q_UINT64_C(0xffff000000000000)); + + int i = 0; + for (; i < count-3; i += 4) { + uint32x4_t vs32 = vld1q_u32(src + i); + uint32x4_t alphaVector = vshrq_n_u32(vs32, 24); +#if defined(Q_PROCESSOR_ARM_64) + uint32_t alphaSum = vaddvq_u32(alphaVector); +#else + // no vaddvq_u32 + uint32x2_t tmp = vpadd_u32(vget_low_u32(alphaVector), vget_high_u32(alphaVector)); + uint32_t alphaSum = vget_lane_u32(vpadd_u32(tmp, tmp), 0); +#endif + if (alphaSum) { + if (!RGBA) + vs32 = vrgba2argb(vs32); + const uint8x16_t vs8 = vreinterpretq_u8_u32(vs32); + const uint8x16x2_t v = vzipq_u8(vs8, vs8); + if (alphaSum != 255 * 4) { + const uint8x8_t s1 = vreinterpret_u8_u32(vget_low_u32(vs32)); + const uint8x8_t s2 = vreinterpret_u8_u32(vget_high_u32(vs32)); + const uint8x8_t alpha1 = vtbl1_u8(s1, shuffleMask); + const uint8x8_t alpha2 = vtbl1_u8(s2, shuffleMask); + uint16x8_t src1 = vmull_u8(s1, alpha1); + uint16x8_t src2 = vmull_u8(s2, alpha2); + // convert from 0->(255x255) to 0->(255x257) + src1 = vsraq_n_u16(src1, src1, 7); + src2 = vsraq_n_u16(src2, src2, 7); + + // now restore alpha from the trivial conversion + const uint64x2_t d1 = vbslq_u64(blendMask, vreinterpretq_u64_u8(v.val[0]), vreinterpretq_u64_u16(src1)); + const uint64x2_t d2 = vbslq_u64(blendMask, vreinterpretq_u64_u8(v.val[1]), vreinterpretq_u64_u16(src2)); + + vst1q_u16((uint16_t *)buffer, vreinterpretq_u16_u64(d1)); + buffer += 2; + vst1q_u16((uint16_t *)buffer, vreinterpretq_u16_u64(d2)); + buffer += 2; + } else { + vst1q_u16((uint16_t *)buffer, vreinterpretq_u16_u8(v.val[0])); + buffer += 2; + vst1q_u16((uint16_t *)buffer, vreinterpretq_u16_u8(v.val[1])); + buffer += 2; + } + } else { + vst1q_u16((uint16_t *)buffer, vdupq_n_u16(0)); + buffer += 2; + vst1q_u16((uint16_t *)buffer, vdupq_n_u16(0)); + buffer += 2; + } + } + + SIMD_EPILOGUE(i, count, 3) { + uint s = src[i]; + if (RGBA) + s = RGBA2ARGB(s); + *buffer++ = QRgba64::fromArgb32(s).premultiplied(); + } +} + static inline float32x4_t reciprocal_mul_ps(float32x4_t a, float mul) { float32x4_t ia = vrecpeq_f32(a); // estimate 1/a @@ -1258,6 +1324,34 @@ const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_neon(uint *buffer, const uchar * return buffer; } +const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count, + const QVector *, QDitherInfo *) +{ + convertARGB32ToRGBA64PM_neon(buffer, src, count); + return buffer; +} + +const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count, + const QVector *, QDitherInfo *) +{ + convertARGB32ToRGBA64PM_neon(buffer, src, count); + return buffer; +} + +const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_neon(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector *, QDitherInfo *) +{ + convertARGB32ToRGBA64PM_neon(buffer, reinterpret_cast(src) + index, count); + return buffer; +} + +const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_neon(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector *, QDitherInfo *) +{ + convertARGB32ToRGBA64PM_neon(buffer, reinterpret_cast(src) + index, count); + return buffer; +} + void QT_FASTCALL storeRGB32FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count, const QVector *, QDitherInfo *) { diff --git a/src/gui/painting/qdrawhelper_sse4.cpp b/src/gui/painting/qdrawhelper_sse4.cpp index 06bfd3465e..9e3e6682bb 100644 --- a/src/gui/painting/qdrawhelper_sse4.cpp +++ b/src/gui/painting/qdrawhelper_sse4.cpp @@ -94,6 +94,55 @@ static void convertARGBToARGB32PM_sse4(uint *buffer, const uint *src, int count) } } +template +static void convertARGBToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int count) +{ + int i = 0; + const __m128i alphaMask = _mm_set1_epi32(0xff000000); + const __m128i rgbaMask = _mm_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15); + const __m128i shuffleMask = _mm_setr_epi8(6, 7, 6, 7, 6, 7, 6, 7, 14, 15, 14, 15, 14, 15, 14, 15); + const __m128i zero = _mm_setzero_si128(); + + for (; i < count - 3; i += 4) { + __m128i srcVector = _mm_loadu_si128((const __m128i *)&src[i]); + if (!_mm_testz_si128(srcVector, alphaMask)) { + if (!_mm_testc_si128(srcVector, alphaMask)) { + if (!RGBA) + srcVector = _mm_shuffle_epi8(srcVector, rgbaMask); + __m128i src1 = _mm_unpacklo_epi8(srcVector, zero); + __m128i src2 = _mm_unpackhi_epi8(srcVector, zero); + __m128i alpha1 = _mm_shuffle_epi8(src1, shuffleMask); + __m128i alpha2 = _mm_shuffle_epi8(src2, shuffleMask); + src1 = _mm_mullo_epi16(src1, alpha1); + src2 = _mm_mullo_epi16(src2, alpha2); + alpha1 = _mm_unpacklo_epi8(srcVector, srcVector); + alpha2 = _mm_unpackhi_epi8(srcVector, srcVector); + src1 = _mm_add_epi16(src1, _mm_srli_epi16(src1, 7)); + src2 = _mm_add_epi16(src2, _mm_srli_epi16(src2, 7)); + src1 = _mm_blend_epi16(src1, alpha1, 0x88); + src2 = _mm_blend_epi16(src2, alpha2, 0x88); + _mm_storeu_si128((__m128i *)&buffer[i], src1); + _mm_storeu_si128((__m128i *)&buffer[i + 2], src2); + } else { + if (!RGBA) + srcVector = _mm_shuffle_epi8(srcVector, rgbaMask); + const __m128i src1 = _mm_unpacklo_epi8(srcVector, srcVector); + const __m128i src2 = _mm_unpackhi_epi8(srcVector, srcVector); + _mm_storeu_si128((__m128i *)&buffer[i], src1); + _mm_storeu_si128((__m128i *)&buffer[i + 2], src2); + } + } else { + _mm_storeu_si128((__m128i *)&buffer[i], zero); + _mm_storeu_si128((__m128i *)&buffer[i + 2], zero); + } + } + + SIMD_EPILOGUE(i, count, 3) { + const uint s = RGBA ? RGBA2ARGB(src[i]) : src[i]; + buffer[i] = QRgba64::fromArgb32(s).premultiplied(); + } +} + static inline __m128 Q_DECL_VECTORCALL reciprocal_mul_ps(__m128 a, float mul) { __m128 ia = _mm_rcp_ps(a); // Approximate 1/a @@ -269,6 +318,20 @@ void QT_FASTCALL convertRGBA8888ToARGB32PM_sse4(uint *buffer, int count, const Q convertARGBToARGB32PM_sse4(buffer, buffer, count); } +const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int count, + const QVector *, QDitherInfo *) +{ + convertARGBToRGBA64PM_sse4(buffer, src, count); + return buffer; +} + +const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int count, + const QVector *, QDitherInfo *) +{ + convertARGBToRGBA64PM_sse4(buffer, src, count); + return buffer; +} + const uint *QT_FASTCALL fetchARGB32ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count, const QVector *, QDitherInfo *) { @@ -283,6 +346,20 @@ const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_sse4(uint *buffer, const uchar * return buffer; } +const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_sse4(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector *, QDitherInfo *) +{ + convertARGBToRGBA64PM_sse4(buffer, reinterpret_cast(src) + index, count); + return buffer; +} + +const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_sse4(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector *, QDitherInfo *) +{ + convertARGBToRGBA64PM_sse4(buffer, reinterpret_cast(src) + index, count); + return buffer; +} + void QT_FASTCALL storeRGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, const QVector *, QDitherInfo *) {