[Pixman] [PATCH] sse2: Using MMX and SSE 4.1

Matt Turner mattst88 at gmail.com
Wed May 2 20:42:23 PDT 2012


I started porting my src_8888_0565 MMX function to SSE2, and in the
process started thinking about using SSE3+. The useful instructions
added post SSE2 that I see are
	SSE3:	lddqu - for unaligned loads across cache lines
	SSSE3:	palignr - for unaligned loads (but requires software
			  pipelining...)
		pmaddubsw - maybe?
	SSE4.1:	pextr*, pinsr*
		pcmpeqq, ptest
		packusdw - for 888 -> 565 packing

I first wrote a basic src_8888_0565 for SSE2 and discovered that the
performance was worse than MMX (which we've been saying has no use in
modern systems -- oops!). I figured the cool pmadd algorithm of MMX was
the cause, but I wondered if 16-byte SSE chunks are too large sometimes.

I added an 8-byte MMX loop before and after the main 16-byte SSE loop
and got a nice improvement.

Porting the pmadd algorithm to SSE4.1 gave another (very large)
improvement.

fast:	src_8888_0565 = L1: 655.18  L2: 675.94  M:642.31  ( 23.44%) HT:403.00  VT:286.45  R:307.61  RT:150.59 (1675Kops/s)
mmx:	src_8888_0565 = L1:2050.45  L2:1988.97  M:1586.16 ( 57.34%) HT:529.12  VT:374.28  R:412.09  RT:177.35 (1913Kops/s)
sse2:	src_8888_0565 = L1:1518.61  L2:1493.10  M:1279.18 ( 46.24%) HT:433.65  VT:314.48  R:349.14  RT:151.84 (1685Kops/s)
sse2mmx:src_8888_0565 = L1:1544.91  L2:1520.83  M:1307.79 ( 47.01%) HT:447.82  VT:326.81  R:379.60  RT:174.07 (1878Kops/s)
sse4:	src_8888_0565 = L1:4654.11  L2:4202.98  M:1885.01 ( 69.35%) HT:540.65  VT:421.04  R:427.73  RT:161.45 (1773Kops/s)
sse4mmx:src_8888_0565 = L1:4786.27  L2:4255.13  M:1920.18 ( 69.93%) HT:581.42  VT:447.99  R:482.27  RT:193.15 (2049Kops/s)

I'd like to isolate exactly what the performance improvement given by
the only SSE4.1 instruction (i.e., _mm_packus_epi32) is before declaring
SSE4.1 a fantastic improvement. If you can come up with a reasonable way
to pack the two xmm registers together in pack_565_2packedx128_128,
please tell me. Shuffle only works on hi/lo 8-bytes, so it'd be a pain.

This got me wondering how to proceed. I'd rather not duplicate a bunch
of code from pixman-mmx.c, and I'd rather not add #ifdef USE_SSE41 to
pixman-sse2.c and make it a compile-time option (or recompile the whole
file to get a few improvements from SSE4.1).

It seems like we need a generic solution that would say for each
compositing function
	- this is what you do for 1-byte;
	- this is what you do for 8-bytes if you have MMX;
	- this is what you do for 16-bytes if you have SSE2;
	- this is what you do for 16-bytes if you have SSE3;
	- this is what you do for 16-bytes if you have SSE4.1.
and then construct the functions for generic/MMX/SSE2/SSE4 at build time.

Does this seem like a reasonable approach? *How* to do it -- suggestions
welcome.
---
 pixman/pixman-sse2.c |  152 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 152 insertions(+), 0 deletions(-)

diff --git a/pixman/pixman-sse2.c b/pixman/pixman-sse2.c
index e217ca3..763c7b3 100644
--- a/pixman/pixman-sse2.c
+++ b/pixman/pixman-sse2.c
@@ -30,8 +30,12 @@
 #include <config.h>
 #endif
 
+#include <mmintrin.h>
 #include <xmmintrin.h> /* for _mm_shuffle_pi16 and _MM_SHUFFLE */
 #include <emmintrin.h> /* for SSE2 intrinsics */
+#if USE_SSE41
+#include <smmintrin.h>
+#endif
 #include "pixman-private.h"
 #include "pixman-combine32.h"
 #include "pixman-inlines.h"
@@ -53,6 +57,9 @@ static __m128i mask_blue;
 static __m128i mask_565_fix_rb;
 static __m128i mask_565_fix_g;
 
+static __m128i mask_565_rb;
+static __m128i mask_565_pack_multiplier;
+
 static force_inline __m128i
 unpack_32_1x128 (uint32_t data)
 {
@@ -120,7 +127,59 @@ pack_2x128_128 (__m128i lo, __m128i hi)
     return _mm_packus_epi16 (lo, hi);
 }
 
+#if USE_X86_MMX
+#define MC(x) ((__m64)mmx_ ## x)
+
+static force_inline __m64
+pack_4xpacked565 (__m64 a, __m64 b)
+{
+    static const uint64_t mmx_565_pack_multiplier = 0x2000000420000004ULL;
+    static const uint64_t mmx_packed_565_rb = 0x00f800f800f800f8ULL;
+    static const uint64_t mmx_packed_565_g = 0x0000fc000000fc00ULL;
+
+    __m64 rb0 = _mm_and_si64 (a, MC (packed_565_rb));
+    __m64 rb1 = _mm_and_si64 (b, MC (packed_565_rb));
+
+    __m64 t0 = _mm_madd_pi16 (rb0, MC (565_pack_multiplier));
+    __m64 t1 = _mm_madd_pi16 (rb1, MC (565_pack_multiplier));
+
+    __m64 g0 = _mm_and_si64 (a, MC (packed_565_g));
+    __m64 g1 = _mm_and_si64 (b, MC (packed_565_g));
+
+    t0 = _mm_or_si64 (t0, g0);
+    t1 = _mm_or_si64 (t1, g1);
+
+    t0 = _mm_srli_si64 (t0, 5);
+    t1 = _mm_slli_si64 (t1, 11);
+    return _mm_shuffle_pi16 (_mm_or_si64 (t0, t1), _MM_SHUFFLE (3, 1, 2, 0));
+}
+#endif
+
+#ifdef USE_SSE41
 static force_inline __m128i
+pack_565_2packedx128_128 (__m128i lo, __m128i hi)
+{
+    __m128i rb0 = _mm_and_si128 (lo, mask_565_rb);
+    __m128i rb1 = _mm_and_si128 (hi, mask_565_rb);
+
+    __m128i t0 = _mm_madd_epi16 (rb0, mask_565_pack_multiplier);
+    __m128i t1 = _mm_madd_epi16 (rb1, mask_565_pack_multiplier);
+
+    __m128i g0 = _mm_and_si128 (lo, mask_green);
+    __m128i g1 = _mm_and_si128 (hi, mask_green);
+
+    t0 = _mm_or_si128 (t0, g0);
+    t1 = _mm_or_si128 (t1, g1);
+
+    t0 = _mm_srli_epi32 (t0, 5);
+    t1 = _mm_srli_epi32 (t1, 5);
+
+    /* XXX: maybe there's a way to do this relatively efficiently with SSE2? */
+    return _mm_packus_epi32 (t0, t1);
+}
+#endif
+
+__m128i
 pack_565_2x128_128 (__m128i lo, __m128i hi)
 {
     __m128i data;
@@ -2832,6 +2891,93 @@ sse2_composite_over_8888_n_8888 (pixman_implementation_t *imp,
 }
 
 static void
+sse2_composite_src_x888_0565 (pixman_implementation_t *imp,
+                              pixman_composite_info_t *info)
+{
+    PIXMAN_COMPOSITE_ARGS (info);
+    uint16_t    *dst_line, *dst;
+    uint32_t    *src_line, *src, s;
+    int dst_stride, src_stride;
+    int32_t w;
+
+    PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1);
+    PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1);
+
+    while (height--)
+    {
+	dst = dst_line;
+	dst_line += dst_stride;
+	src = src_line;
+	src_line += src_stride;
+	w = width;
+
+	while (w && (unsigned long)dst & 7)
+	{
+	    s = *src++;
+	    *dst = CONVERT_8888_TO_0565 (s);
+	    dst++;
+	    w--;
+	}
+
+#if USE_X86_MMX
+	while (w >= 4 && (unsigned long)dst & 15)
+	{
+	    __m64 vsrc0 = *(__m64 *)(src + 0);
+	    __m64 vsrc1 = *(__m64 *)(src + 2);
+
+	    *(__m64 *)dst = pack_4xpacked565 (vsrc0, vsrc1);
+
+	    w -= 4;
+	    src += 4;
+	    dst += 4;
+	}
+#endif
+
+	while (w >= 8)
+	{
+	    __m128i xmm_src0 = load_128_unaligned ((__m128i *)src + 0);
+	    __m128i xmm_src1 = load_128_unaligned ((__m128i *)src + 1);
+
+#if USE_SSE41
+	    save_128_aligned ((__m128i*)dst, pack_565_2packedx128_128 (xmm_src0, xmm_src1));
+#else
+	    __m128i xmm_src0_lo, xmm_src0_hi, xmm_src1_lo, xmm_src1_hi;
+	    unpack_128_2x128 (xmm_src0, &xmm_src0_lo, &xmm_src0_hi);
+	    unpack_128_2x128 (xmm_src1, &xmm_src1_lo, &xmm_src1_hi);
+
+	    save_128_aligned ((__m128i*)dst, pack_565_4x128_128 (&xmm_src0_lo, &xmm_src0_hi, &xmm_src1_lo, &xmm_src1_hi));
+#endif
+
+	    w -= 8;
+	    src += 8;
+	    dst += 8;
+	}
+
+#if USE_X86_MMX
+	while (w >= 4)
+	{
+	    __m64 vsrc0 = *(__m64 *)(src + 0);
+	    __m64 vsrc1 = *(__m64 *)(src + 2);
+
+	    *(__m64 *)dst = pack_4xpacked565 (vsrc0, vsrc1);
+
+	    w -= 4;
+	    src += 4;
+	    dst += 4;
+	}
+#endif
+
+	while (w)
+	{
+	    s = *src++;
+	    *dst = CONVERT_8888_TO_0565 (s);
+	    dst++;
+	    w--;
+	}
+    }
+}
+
+static void
 sse2_composite_src_x888_8888 (pixman_implementation_t *imp,
 			      pixman_composite_info_t *info)
 {
@@ -5727,6 +5873,10 @@ static const pixman_fast_path_t sse2_fast_paths[] =
     PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8r8g8b8, sse2_composite_src_n_8_8888),
     PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8b8g8r8, sse2_composite_src_n_8_8888),
     PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8b8g8r8, sse2_composite_src_n_8_8888),
+    PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, r5g6b5, sse2_composite_src_x888_0565),
+    PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, b5g6r5, sse2_composite_src_x888_0565),
+    PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, r5g6b5, sse2_composite_src_x888_0565),
+    PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, b5g6r5, sse2_composite_src_x888_0565),
     PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, a8r8g8b8, sse2_composite_src_x888_8888),
     PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, a8b8g8r8, sse2_composite_src_x888_8888),
     PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, a8r8g8b8, sse2_composite_copy_area),
@@ -6035,6 +6185,8 @@ _pixman_implementation_create_sse2 (pixman_implementation_t *fallback)
     mask_ffff = create_mask_16_128 (0xffff);
     mask_ff000000 = create_mask_2x32_128 (0xff000000, 0xff000000);
     mask_alpha = create_mask_2x32_128 (0x00ff0000, 0x00000000);
+    mask_565_rb = create_mask_2x32_128 (0x00f800f8, 0x00f800f8);
+    mask_565_pack_multiplier = create_mask_2x32_128 (0x20000004, 0x20000004);
 
     /* Set up function pointers */
     imp->combine_32[PIXMAN_OP_OVER] = sse2_combine_over_u;
-- 
1.7.3.4



More information about the Pixman mailing list