/* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkBlitRect_opts_SSE2.h" #include "SkBlitRow.h" #include "SkColorPriv.h" #include <emmintrin.h> /** Simple blitting of opaque rectangles less than 31 pixels wide: inlines and merges sections of Color32_SSE2 and sk_memset32_SSE2. */ static void BlitRect32_OpaqueNarrow_SSE2(SkPMColor* SK_RESTRICT destination, int width, int height, size_t rowBytes, uint32_t color) { SkASSERT(255 == SkGetPackedA32(color)); SkASSERT(width > 0); SkASSERT(width < 31); while (--height >= 0) { SkPMColor* dst = destination; int count = width; while (count > 4) { *dst++ = color; *dst++ = color; *dst++ = color; *dst++ = color; count -= 4; } while (count > 0) { *dst++ = color; --count; } destination = (uint32_t*)((char*)destination + rowBytes); } } /** Fast blitting of opaque rectangles at least 31 pixels wide: inlines and merges sections of Color32_SSE2 and sk_memset32_SSE2. A 31 pixel rectangle is guaranteed to have at least one 16-pixel aligned span that can take advantage of mm_store. */ static void BlitRect32_OpaqueWide_SSE2(SkPMColor* SK_RESTRICT destination, int width, int height, size_t rowBytes, uint32_t color) { SkASSERT(255 == SkGetPackedA32(color)); SkASSERT(width >= 31); __m128i color_wide = _mm_set1_epi32(color); while (--height >= 0) { // Prefetching one row ahead to L1 cache can equal hardware // performance for large/tall rects, but never *beats* // hardware performance. SkPMColor* dst = destination; int count = width; while (((size_t)dst) & 0x0F) { *dst++ = color; --count; } __m128i *d = reinterpret_cast<__m128i*>(dst); // Googling suggests _mm_stream is only going to beat _mm_store // for things that wouldn't fit in L2 cache anyway, typically // >500kB, and precisely fill cache lines. For us, with // arrays > 100k elements _mm_stream is still 100%+ slower than // mm_store. // Unrolling to count >= 64 is a break-even for most // input patterns; we seem to be saturating the bus and having // low enough overhead at 32. while (count >= 32) { _mm_store_si128(d++, color_wide); _mm_store_si128(d++, color_wide); _mm_store_si128(d++, color_wide); _mm_store_si128(d++, color_wide); _mm_store_si128(d++, color_wide); _mm_store_si128(d++, color_wide); _mm_store_si128(d++, color_wide); _mm_store_si128(d++, color_wide); count -= 32; } if (count >= 16) { _mm_store_si128(d++, color_wide); _mm_store_si128(d++, color_wide); _mm_store_si128(d++, color_wide); _mm_store_si128(d++, color_wide); count -= 16; } dst = reinterpret_cast<uint32_t*>(d); // Unrolling the loop in the Narrow code is a significant performance // gain, but unrolling this loop appears to make no difference in // benchmarks with either mm_store_si128 or individual sets. while (count > 0) { *dst++ = color; --count; } destination = (uint32_t*)((char*)destination + rowBytes); } } void ColorRect32_SSE2(SkPMColor* destination, int width, int height, size_t rowBytes, uint32_t color) { if (0 == height || 0 == width || 0 == color) { return; } unsigned colorA = SkGetPackedA32(color); colorA = 0; // skip below if () for now...(has been disabled since this was added in r3423). if (255 == colorA) { if (width < 31) { BlitRect32_OpaqueNarrow_SSE2(destination, width, height, rowBytes, color); } else { BlitRect32_OpaqueWide_SSE2(destination, width, height, rowBytes, color); } } else { SkBlitRow::ColorRect32(destination, width, height, rowBytes, color); } }