From 1dd0c4bf1af1c90fde1449a81d41acbc62cf1934 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Fri, 26 Feb 2016 16:53:24 +0100 Subject: [PATCH] SSSE3 optimized store of 24-bit formats Using shuffle and align storing our quint24 format can be done much faster. This in particular improves conversions to RGB888. Change-Id: I179748706a33a43fd6f60f5c40287317418c8867 Reviewed-by: Gunnar Sletta --- src/gui/painting/qdrawhelper.cpp | 4 +- src/gui/painting/qdrawhelper_p.h | 2 +- src/gui/painting/qdrawhelper_ssse3.cpp | 57 +++++++++++++++++++ .../qimageconversion/tst_qimageconversion.cpp | 44 ++++++++++---- 4 files changed, 93 insertions(+), 14 deletions(-) diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 1143123717..112dbd4738 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -961,7 +961,7 @@ const FetchPixelsFunc qFetchPixels[QPixelLayout::BPPCount] = { fetchPixels // BPP32 }; -const StorePixelsFunc qStorePixels[QPixelLayout::BPPCount] = { +StorePixelsFunc qStorePixels[QPixelLayout::BPPCount] = { 0, // BPPNone storePixels, // BPP1MSB storePixels, // BPP1LSB @@ -6375,10 +6375,12 @@ static void qInitDrawhelperFunctions() int w, int h, int const_alpha); + extern void QT_FASTCALL storePixelsBPP24_ssse3(uchar *dest, const uint *src, int index, int count); qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; + qStorePixels[QPixelLayout::BPP24] = storePixelsBPP24_ssse3; } #endif // SSSE3 diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index af52ed0b43..21af6039f8 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -1213,7 +1213,7 @@ typedef void (QT_FASTCALL *StorePixelsFunc)(uchar *dest, const uint *src, int in extern QPixelLayout qPixelLayouts[QImage::NImageFormats]; extern const FetchPixelsFunc qFetchPixels[QPixelLayout::BPPCount]; -extern const StorePixelsFunc qStorePixels[QPixelLayout::BPPCount]; +extern StorePixelsFunc qStorePixels[QPixelLayout::BPPCount]; diff --git a/src/gui/painting/qdrawhelper_ssse3.cpp b/src/gui/painting/qdrawhelper_ssse3.cpp index 09b25d30fd..e0d1bac6b1 100644 --- a/src/gui/painting/qdrawhelper_ssse3.cpp +++ b/src/gui/painting/qdrawhelper_ssse3.cpp @@ -176,6 +176,63 @@ void qt_blend_argb32_on_argb32_ssse3(uchar *destPixels, int dbpl, } } +static inline void store_uint24_ssse3(uchar *dst, const uint *src, int len) +{ + int i = 0; + + quint24 *dst24 = reinterpret_cast(dst); + // Align dst on 16 bytes + for (; i < len && (reinterpret_cast(dst24) & 0xf); ++i) + *dst24++ = quint24(*src++); + + // Shuffle masks for first and second half of every output, all outputs are aligned so the shuffled ends are not used. + const __m128i shuffleMask1 = _mm_setr_epi8(char(0x80), char(0x80), char(0x80), char(0x80), 2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12); + const __m128i shuffleMask2 = _mm_setr_epi8(2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, char(0x80), char(0x80), char(0x80), char(0x80)); + + const __m128i *inVectorPtr = (const __m128i *)src; + __m128i *dstVectorPtr = (__m128i *)dst24; + + for (; i < (len - 15); i += 16) { + // Load four vectors, store three. + // Create each output vector by combining two shuffled input vectors. + __m128i srcVector1 = _mm_loadu_si128(inVectorPtr); + ++inVectorPtr; + __m128i srcVector2 = _mm_loadu_si128(inVectorPtr); + ++inVectorPtr; + __m128i outputVector1 = _mm_shuffle_epi8(srcVector1, shuffleMask1); + __m128i outputVector2 = _mm_shuffle_epi8(srcVector2, shuffleMask2); + __m128i outputVector = _mm_alignr_epi8(outputVector2, outputVector1, 4); + _mm_store_si128(dstVectorPtr, outputVector); + ++dstVectorPtr; + + srcVector1 = _mm_loadu_si128(inVectorPtr); + ++inVectorPtr; + outputVector1 = _mm_shuffle_epi8(srcVector2, shuffleMask1); + outputVector2 = _mm_shuffle_epi8(srcVector1, shuffleMask2); + outputVector = _mm_alignr_epi8(outputVector2, outputVector1, 8); + _mm_store_si128(dstVectorPtr, outputVector); + ++dstVectorPtr; + + srcVector2 = _mm_loadu_si128(inVectorPtr); + ++inVectorPtr; + outputVector1 = _mm_shuffle_epi8(srcVector1, shuffleMask1); + outputVector2 = _mm_shuffle_epi8(srcVector2, shuffleMask2); + outputVector = _mm_alignr_epi8(outputVector2, outputVector1, 12); + _mm_store_si128(dstVectorPtr, outputVector); + ++dstVectorPtr; + } + dst24 = reinterpret_cast(dstVectorPtr); + src = reinterpret_cast(inVectorPtr); + + for (; i < len; ++i) + *dst24++ = quint24(*src++); +} + +void QT_FASTCALL storePixelsBPP24_ssse3(uchar *dest, const uint *src, int index, int count) +{ + store_uint24_ssse3(dest + index * 3, src, count); +} + QT_END_NAMESPACE #endif // QT_COMPILER_SUPPORTS_SSSE3 diff --git a/tests/benchmarks/gui/image/qimageconversion/tst_qimageconversion.cpp b/tests/benchmarks/gui/image/qimageconversion/tst_qimageconversion.cpp index f631688870..31c5520b55 100644 --- a/tests/benchmarks/gui/image/qimageconversion/tst_qimageconversion.cpp +++ b/tests/benchmarks/gui/image/qimageconversion/tst_qimageconversion.cpp @@ -90,10 +90,8 @@ void tst_QImageConversion::convertRgb888ToRgb32() QFETCH(QImage, inputImage); QBENCHMARK { - volatile QImage output = inputImage.convertToFormat(QImage::Format_RGB32); - // we need the volatile and the following to make sure the compiler does not do - // anything stupid :) - (void)output; + QImage output = inputImage.convertToFormat(QImage::Format_RGB32); + output.constBits(); } } @@ -107,10 +105,8 @@ void tst_QImageConversion::convertRgb888ToRgbx8888() QFETCH(QImage, inputImage); QBENCHMARK { - volatile QImage output = inputImage.convertToFormat(QImage::Format_RGBX8888); - // we need the volatile and the following to make sure the compiler does not do - // anything stupid :) - (void)output; + QImage output = inputImage.convertToFormat(QImage::Format_RGBX8888); + output.constBits(); } } @@ -140,10 +136,8 @@ void tst_QImageConversion::convertRgb32ToRgb888() QFETCH(QImage, inputImage); QBENCHMARK { - volatile QImage output = inputImage.convertToFormat(QImage::Format_RGB888); - // we need the volatile and the following to make sure the compiler does not do - // anything stupid :) - (void)output; + QImage output = inputImage.convertToFormat(QImage::Format_RGB888); + output.constBits(); } } @@ -157,6 +151,8 @@ void tst_QImageConversion::convertRgb16_data() QTest::newRow("rgb888") << rgb16 << QImage::Format_RGB888; QTest::newRow("rgb666") << rgb16 << QImage::Format_RGB666; QTest::newRow("rgb555") << rgb16 << QImage::Format_RGB555; + QTest::newRow("argb8565") << rgb16 << QImage::Format_ARGB8565_Premultiplied; + QTest::newRow("argb8555") << rgb16 << QImage::Format_ARGB8555_Premultiplied; } void tst_QImageConversion::convertRgb16() @@ -189,6 +185,7 @@ void tst_QImageConversion::convertRgb32_data() QTest::newRow("rgb32 -> rgb888") << rgb32 << QImage::Format_RGB888; QTest::newRow("rgb32 -> rgb666") << rgb32 << QImage::Format_RGB666; QTest::newRow("rgb32 -> rgb555") << rgb32 << QImage::Format_RGB555; + QTest::newRow("rgb32 -> argb8565pm") << rgb32 << QImage::Format_ARGB8565_Premultiplied; QTest::newRow("argb32 -> rgb16") << argb32 << QImage::Format_RGB16; QTest::newRow("argb32 -> rgb32") << argb32 << QImage::Format_RGB32; @@ -237,12 +234,14 @@ void tst_QImageConversion::convertGeneric_data() QImage rgba32 = argb32.convertToFormat(QImage::Format_RGBA8888); QImage bgr30 = rgb32.convertToFormat(QImage::Format_BGR30); QImage a2rgb30 = argb32.convertToFormat(QImage::Format_A2RGB30_Premultiplied); + QImage rgb666 = rgb32.convertToFormat(QImage::Format_RGB666); QTest::newRow("rgba8888 -> rgb32") << rgba32 << QImage::Format_RGB32; QTest::newRow("rgba8888 -> argb32") << rgba32 << QImage::Format_ARGB32; QTest::newRow("rgba8888 -> argb32pm") << rgba32 << QImage::Format_ARGB32_Premultiplied; QTest::newRow("rgba8888 -> rgbx8888") << rgba32 << QImage::Format_RGBX8888; QTest::newRow("rgba8888 -> rgba8888pm") << rgba32 << QImage::Format_RGBA8888_Premultiplied; + QTest::newRow("rgba8888 -> rgb888") << rgba32 << QImage::Format_RGB888; QTest::newRow("rgba8888 -> rgb30") << rgba32 << QImage::Format_RGB30; QTest::newRow("rgba8888 -> a2bgr30") << rgba32 << QImage::Format_A2BGR30_Premultiplied; @@ -264,6 +263,14 @@ void tst_QImageConversion::convertGeneric_data() QTest::newRow("a2rgb30 -> rgb30") << a2rgb30 << QImage::Format_RGB30; QTest::newRow("a2rgb30 -> bgr30") << a2rgb30 << QImage::Format_BGR30; QTest::newRow("a2rgb30 -> a2bgr30") << a2rgb30 << QImage::Format_A2BGR30_Premultiplied; + + QTest::newRow("rgb666 -> rgb32") << rgb666 << QImage::Format_RGB32; + QTest::newRow("rgb666 -> argb32") << rgb666 << QImage::Format_ARGB32; + QTest::newRow("rgb666 -> argb32pm") << rgb666 << QImage::Format_ARGB32_Premultiplied; + QTest::newRow("rgb666 -> rgb888") << rgb666 << QImage::Format_RGB888; + QTest::newRow("rgb666 -> rgb16") << rgb666 << QImage::Format_RGB16; + QTest::newRow("rgb666 -> rgb555") << rgb666 << QImage::Format_RGB555; + QTest::newRow("rgb666 -> rgb30") << rgb666 << QImage::Format_RGB30; } void tst_QImageConversion::convertGeneric() @@ -285,6 +292,9 @@ void tst_QImageConversion::convertGenericInplace_data() QImage argb32 = generateImageArgb32(1000, 1000); QImage argb32pm = argb32.convertToFormat(QImage::Format_ARGB32_Premultiplied); QImage rgba8888 = argb32.convertToFormat(QImage::Format_RGBA8888); + QImage argb6666 = argb32.convertToFormat(QImage::Format_ARGB6666_Premultiplied); + QImage argb4444 = argb32.convertToFormat(QImage::Format_ARGB4444_Premultiplied); + QImage rgb16 = argb32.convertToFormat(QImage::Format_RGB16); QTest::newRow("argb32 -> argb32pm -> argb32") << argb32 << QImage::Format_ARGB32_Premultiplied; QTest::newRow("argb32 -> rgb32 -> argb32") << argb32 << QImage::Format_RGB32; @@ -301,6 +311,16 @@ void tst_QImageConversion::convertGenericInplace_data() QTest::newRow("rgba8888 -> rgb32 -> rgba8888") << rgba8888 << QImage::Format_RGB32; QTest::newRow("rgba8888 -> argb32pm -> rgba8888") << rgba8888 << QImage::Format_ARGB32_Premultiplied; QTest::newRow("rgba8888 -> rgba8888pm -> rgba8888") << rgba8888 << QImage::Format_RGBA8888_Premultiplied; + + QTest::newRow("argb6666pm -> argb8565pm -> argb6666pm") << argb6666 << QImage::Format_ARGB8565_Premultiplied; + QTest::newRow("argb6666pm -> rgb888 -> argb6666pm") << argb6666 << QImage::Format_RGB888; + + QTest::newRow("argb4444pm -> rgb16 -> argb4444pm") << argb4444 << QImage::Format_RGB16; + QTest::newRow("argb4444pm -> rgb444 -> argb4444pm") << argb4444 << QImage::Format_RGB444; + + QTest::newRow("rgb16 -> rgb555 -> rgb16") << rgb16 << QImage::Format_RGB555; + QTest::newRow("rgb16 -> rgb444 -> rgb16") << rgb16 << QImage::Format_RGB444; + QTest::newRow("rgb16 -> argb4444pm -> rgb16") << rgb16 << QImage::Format_ARGB4444_Premultiplied; } void tst_QImageConversion::convertGenericInplace()