From 0c5d9b953e0a5ba9ed41fb897cf04b527436a300 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Tue, 26 Jan 2021 17:14:15 +0100 Subject: [PATCH] Optimize RGBA64PM->RGBA64 for SSE4 And remove direct conversion to take advantage of both the SIMD, but also threading on all platforms. Change-Id: I782ae7c80e82b694e23abee28d69cecd0d28c32f Reviewed-by: Thiago Macieira --- src/gui/image/qimage_conversions.cpp | 56 --------------- src/gui/painting/qdrawhelper.cpp | 4 ++ src/gui/painting/qdrawhelper_sse4.cpp | 98 +++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 56 deletions(-) diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index f72184fcde..84a5a5ab83 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -1259,55 +1259,6 @@ static bool convert_RGBA64_to_RGBx64_inplace(QImageData *data, Qt::ImageConversi return true; } -template -static void convert_RGBA64PM_to_RGBA64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied); - Q_ASSERT(dest->format == QImage::Format_RGBA64 || dest->format == QImage::Format_RGBX64); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 3) - src->width; - const int dest_pad = (dest->bytes_per_line >> 3) - dest->width; - const QRgba64 *src_data = reinterpret_cast(src->data); - QRgba64 *dest_data = reinterpret_cast(dest->data); - - for (int i = 0; i < src->height; ++i) { - const QRgba64 *end = src_data + src->width; - while (src_data < end) { - *dest_data = src_data->unpremultiplied(); - if (MaskAlpha) - dest_data->setAlpha(65535); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -template -static bool convert_RGBA64PM_to_RGBA64_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_RGBA64_Premultiplied); - - const int pad = (data->bytes_per_line >> 3) - data->width; - QRgba64 *rgb_data = reinterpret_cast(data->data); - - for (int i = 0; i < data->height; ++i) { - const QRgba64 *end = rgb_data + data->width; - while (rgb_data < end) { - *rgb_data = rgb_data->unpremultiplied(); - if (MaskAlpha) - rgb_data->setAlpha(65535); - ++rgb_data; - } - rgb_data += pad; - } - data->format = MaskAlpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64; - return true; -} - static void convert_gray16_to_RGBA64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) { Q_ASSERT(src->format == QImage::Format_Grayscale16); @@ -2326,8 +2277,6 @@ static void qInitImageConversions() qimage_converter_map[QImage::Format_RGBA64][QImage::Format_RGBA8888] = convert_RGBA64_to_ARGB32; qimage_converter_map[QImage::Format_RGBA64][QImage::Format_RGBX64] = convert_RGBA64_to_RGBx64; - qimage_converter_map[QImage::Format_RGBA64_Premultiplied][QImage::Format_RGBX64] = convert_RGBA64PM_to_RGBA64; - qimage_converter_map[QImage::Format_RGBA64_Premultiplied][QImage::Format_RGBA64] = convert_RGBA64PM_to_RGBA64; qimage_converter_map[QImage::Format_RGBA64_Premultiplied][QImage::Format_Grayscale16] = convert_RGBA64_to_gray16; qimage_converter_map[QImage::Format_Grayscale16][QImage::Format_RGBX64] = convert_gray16_to_RGBA64; @@ -2443,11 +2392,6 @@ static void qInitImageConversions() qimage_inplace_converter_map[QImage::Format_RGBA64][QImage::Format_RGBX64] = convert_RGBA64_to_RGBx64_inplace; - qimage_inplace_converter_map[QImage::Format_RGBA64_Premultiplied][QImage::Format_RGBX64] = - convert_RGBA64PM_to_RGBA64_inplace; - qimage_inplace_converter_map[QImage::Format_RGBA64_Premultiplied][QImage::Format_RGBA64] = - convert_RGBA64PM_to_RGBA64_inplace; - qimage_inplace_converter_map[QImage::Format_BGR888][QImage::Format_RGB888] = convert_rgbswap_generic_inplace; diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index f28e8a6c59..c66268c5f3 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -5141,6 +5141,8 @@ static void qInitDrawhelperFunctions() const QList *, QDitherInfo *); extern void QT_FASTCALL storeRGBA8888FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count, const QList *, QDitherInfo *); + extern void QT_FASTCALL storeRGBA64FromRGBA64PM_sse4(uchar *, const QRgba64 *, int, int, const QList *, QDitherInfo *); + extern void QT_FASTCALL storeRGBx64FromRGBA64PM_sse4(uchar *, const QRgba64 *, int, int, const QList *, QDitherInfo *); extern void QT_FASTCALL destStore64ARGB32_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length); extern void QT_FASTCALL destStore64RGBA8888_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length); # ifndef __AVX2__ @@ -5162,6 +5164,8 @@ static void qInitDrawhelperFunctions() qPixelLayouts[QImage::Format_A2RGB30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4; qStoreFromRGBA64PM[QImage::Format_ARGB32] = storeARGB32FromRGBA64PM_sse4; qStoreFromRGBA64PM[QImage::Format_RGBA8888] = storeRGBA8888FromRGBA64PM_sse4; + qStoreFromRGBA64PM[QImage::Format_RGBX64] = storeRGBx64FromRGBA64PM_sse4; + qStoreFromRGBA64PM[QImage::Format_RGBA64] = storeRGBA64FromRGBA64PM_sse4; #if QT_CONFIG(raster_64bit) destStoreProc64[QImage::Format_ARGB32] = destStore64ARGB32_sse4; destStoreProc64[QImage::Format_RGBA8888] = destStore64RGBA8888_sse4; diff --git a/src/gui/painting/qdrawhelper_sse4.cpp b/src/gui/painting/qdrawhelper_sse4.cpp index 8ea8367c3a..dc9755e414 100644 --- a/src/gui/painting/qdrawhelper_sse4.cpp +++ b/src/gui/painting/qdrawhelper_sse4.cpp @@ -327,6 +327,90 @@ static inline void convertARGBFromRGBA64PM_sse4(uint *buffer, const QRgba64 *src } } +template +static inline void convertRGBA64FromRGBA64PM_sse4(QRgba64 *buffer, const QRgba64 *src, int count) +{ + int i = 0; + if ((_MM_GET_EXCEPTION_MASK() & _MM_MASK_INVALID) == 0) { + for (; i < count; ++i) { + QRgba64 v = src[i].unpremultiplied(); + if (mask) + v.setAlpha(65535); + buffer[i] = v; + } + return; + } + const __m128i alphaMask = _mm_set1_epi64x(qint64(Q_UINT64_C(0xffff) << 48)); + const __m128i zero = _mm_setzero_si128(); + + for (; i < count - 3; i += 4) { + __m128i srcVector1 = _mm_loadu_si128((const __m128i *)&src[i + 0]); + __m128i srcVector2 = _mm_loadu_si128((const __m128i *)&src[i + 2]); + bool transparent1 = _mm_testz_si128(srcVector1, alphaMask); + bool opaque1 = _mm_testc_si128(srcVector1, alphaMask); + bool transparent2 = _mm_testz_si128(srcVector2, alphaMask); + bool opaque2 = _mm_testc_si128(srcVector2, alphaMask); + + if (!(transparent1 && transparent2)) { + if (!(opaque1 && opaque2)) { + __m128i srcVector1Alpha = _mm_srli_epi64(srcVector1, 48); + __m128i srcVector2Alpha = _mm_srli_epi64(srcVector2, 48); + __m128i srcVectorAlpha = _mm_packus_epi32(srcVector1Alpha, srcVector2Alpha); + const __m128 a = _mm_cvtepi32_ps(srcVectorAlpha); + const __m128 ia = reciprocal_mul_ps(a, 65535.0f); + __m128i src1 = _mm_unpacklo_epi16(srcVector1, zero); + __m128i src2 = _mm_unpackhi_epi16(srcVector1, zero); + __m128i src3 = _mm_unpacklo_epi16(srcVector2, zero); + __m128i src4 = _mm_unpackhi_epi16(srcVector2, zero); + __m128 ia1 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 ia2 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(1, 1, 1, 1)); + __m128 ia3 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 ia4 = _mm_shuffle_ps(ia, ia, _MM_SHUFFLE(3, 3, 3, 3)); + src1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src1), ia1)); + src2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src2), ia2)); + src3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src3), ia3)); + src4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(src4), ia4)); + src1 = _mm_packus_epi32(src1, src2); + src3 = _mm_packus_epi32(src3, src4); + // Handle potential alpha == 0 values: + __m128i srcVector1AlphaMask = _mm_cmpeq_epi64(srcVector1Alpha, zero); + __m128i srcVector2AlphaMask = _mm_cmpeq_epi64(srcVector2Alpha, zero); + src1 = _mm_andnot_si128(srcVector1AlphaMask, src1); + src3 = _mm_andnot_si128(srcVector2AlphaMask, src3); + // Fixup alpha values: + if (mask) { + src1 = _mm_or_si128(src1, alphaMask); + src3 = _mm_or_si128(src3, alphaMask); + } else { + src1 = _mm_blendv_epi8(src1, srcVector1, alphaMask); + src3 = _mm_blendv_epi8(src3, srcVector2, alphaMask); + } + _mm_storeu_si128((__m128i *)&buffer[i + 0], src1); + _mm_storeu_si128((__m128i *)&buffer[i + 2], src3); + } else { + if (mask) { + srcVector1 = _mm_or_si128(srcVector1, alphaMask); + srcVector2 = _mm_or_si128(srcVector2, alphaMask); + } + if (mask || src != buffer) { + _mm_storeu_si128((__m128i *)&buffer[i + 0], srcVector1); + _mm_storeu_si128((__m128i *)&buffer[i + 2], srcVector2); + } + } + } else { + _mm_storeu_si128((__m128i *)&buffer[i + 0], zero); + _mm_storeu_si128((__m128i *)&buffer[i + 2], zero); + } + } + + SIMD_EPILOGUE(i, count, 3) { + QRgba64 v = src[i].unpremultiplied(); + if (mask) + v.setAlpha(65535); + buffer[i] = v; + } +} + #ifndef __AVX2__ void QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, int count, const QList *) { @@ -446,6 +530,20 @@ void QT_FASTCALL storeRGBA8888FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, convertARGBFromRGBA64PM_sse4(d, src, count); } +void QT_FASTCALL storeRGBA64FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count, + const QList *, QDitherInfo *) +{ + QRgba64 *d = (QRgba64 *)dest + index; + convertRGBA64FromRGBA64PM_sse4(d, src, count); +} + +void QT_FASTCALL storeRGBx64FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count, + const QList *, QDitherInfo *) +{ + QRgba64 *d = (QRgba64 *)dest + index; + convertRGBA64FromRGBA64PM_sse4(d, src, count); +} + template void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, const QList *, QDitherInfo *);