mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 17:28:49 -10:00
rpng: real SSE2 kernels for the scanline conversions
The _sse2-suffixed conversion helpers contained no intrinsics - they were 4x-unrolled scalar loops. Replace both with genuine SSE2: RGB->32bpp expands 4 pixels per 16-byte load. SSE2 has no byte shuffle (pshufb is SSSE3), but the fixed 3->4 byte expansion falls out of whole-register byte shifts plus dword masks, placing triple k at output byte 4k with alpha OR'd in; the ARGB order additionally swaps R and B inside each word with the usual mask/shift trick. The load reads 4 bytes past the 12 consumed, so the vector loop requires 6 remaining pixels (18 bytes) and hands the tail to the scalar loop. RGBA->32bpp: the supports_rgba case is the input bytes verbatim on LE (implied by SSE2/x86), so it is a straight row copy, matching the dispatcher-level fast path; the ARGB case is a vectorized R/B swap within each word. Output is byte-identical to the previous code for all color types at both supports_rgba values. 1920x1080 RGB full decode: ~9% faster at either output order.
This commit is contained in:
1 parent
154e920b82
commit
211263f008
1 file changed
+51
-66
@@ -427,33 +427,48 @@ static void rpng_filter_paeth_rgba(uint8_t *decoded,
|
||||
* -------------------------------------------------------------------------*/
|
||||
|
||||
/* Pack 8-bit RGB triples into ARGB32/ABGR32 words (alpha = 0xFF).
|
||||
* SSE2 version processes 4 pixels (12 input bytes) per iteration. */
|
||||
* SSE2 version expands 4 pixels (12 input bytes) per 16-byte load:
|
||||
* SSE2 has no byte shuffle (pshufb is SSSE3), but the fixed 3->4 byte
|
||||
* expansion falls out of whole-register byte shifts plus dword masks -
|
||||
* w = (v & M0) | (v<<1B & M1) | (v<<2B & M2) | (v<<3B & M3)
|
||||
* places triple k at output byte 4k, giving memory order R,G,B after
|
||||
* the alpha OR (the ABGR32 supports_rgba layout on LE); the ARGB
|
||||
* layout additionally swaps R and B inside each word. The load reads
|
||||
* 4 bytes past the 12 consumed, so the vector loop requires at least
|
||||
* 6 pixels (18 bytes) of remaining scanline. */
|
||||
#if defined(RPNG_SIMD_SSE2)
|
||||
static void rpng_copy_line_rgb_sse2(uint32_t *data,
|
||||
const uint8_t *src, unsigned width, bool supports_rgba)
|
||||
{
|
||||
unsigned i = 0;
|
||||
const __m128i m0 = _mm_setr_epi32((int)0x00FFFFFF, 0, 0, 0);
|
||||
const __m128i m1 = _mm_setr_epi32(0, (int)0x00FFFFFF, 0, 0);
|
||||
const __m128i m2 = _mm_setr_epi32(0, 0, (int)0x00FFFFFF, 0);
|
||||
const __m128i m3 = _mm_setr_epi32(0, 0, 0, (int)0x00FFFFFF);
|
||||
const __m128i ma = _mm_set1_epi32((int)0xFF000000u);
|
||||
const __m128i keep = _mm_set1_epi32((int)0xFF00FF00u);
|
||||
const __m128i lowm = _mm_set1_epi32(0xFF);
|
||||
|
||||
for (; (int)(width - i) >= 6; i += 4)
|
||||
{
|
||||
__m128i v = _mm_loadu_si128((const __m128i*)(src + (size_t)i * 3));
|
||||
__m128i w = _mm_or_si128(
|
||||
_mm_or_si128(_mm_and_si128(v, m0),
|
||||
_mm_and_si128(_mm_slli_si128(v, 1), m1)),
|
||||
_mm_or_si128(_mm_and_si128(_mm_slli_si128(v, 2), m2),
|
||||
_mm_and_si128(_mm_slli_si128(v, 3), m3)));
|
||||
if (!supports_rgba)
|
||||
{
|
||||
/* memory R,G,B -> B,G,R: swap the low and high channel bytes */
|
||||
__m128i lo = _mm_slli_epi32(_mm_and_si128(w, lowm), 16);
|
||||
__m128i hi = _mm_and_si128(_mm_srli_epi32(w, 16), lowm);
|
||||
w = _mm_or_si128(_mm_and_si128(w, keep), _mm_or_si128(lo, hi));
|
||||
}
|
||||
_mm_storeu_si128((__m128i*)(data + i), _mm_or_si128(w, ma));
|
||||
}
|
||||
|
||||
if (supports_rgba)
|
||||
{
|
||||
for (; (int)(width - i) >= 4; i += 4)
|
||||
{
|
||||
data[i + 0] = 0xFF000000u
|
||||
| ((unsigned)src[i*3+2] << 16)
|
||||
| ((unsigned)src[i*3+1] << 8)
|
||||
| ((unsigned)src[i*3+0] );
|
||||
data[i + 1] = 0xFF000000u
|
||||
| ((unsigned)src[i*3+5] << 16)
|
||||
| ((unsigned)src[i*3+4] << 8)
|
||||
| ((unsigned)src[i*3+3] );
|
||||
data[i + 2] = 0xFF000000u
|
||||
| ((unsigned)src[i*3+8] << 16)
|
||||
| ((unsigned)src[i*3+7] << 8)
|
||||
| ((unsigned)src[i*3+6] );
|
||||
data[i + 3] = 0xFF000000u
|
||||
| ((unsigned)src[i*3+11] << 16)
|
||||
| ((unsigned)src[i*3+10] << 8)
|
||||
| ((unsigned)src[i*3+9] );
|
||||
}
|
||||
for (; i < width; i++)
|
||||
data[i] = 0xFF000000u
|
||||
| ((unsigned)src[i*3+2] << 16)
|
||||
@@ -462,25 +477,6 @@ static void rpng_copy_line_rgb_sse2(uint32_t *data,
|
||||
}
|
||||
else
|
||||
{
|
||||
for (; (int)(width - i) >= 4; i += 4)
|
||||
{
|
||||
data[i + 0] = 0xFF000000u
|
||||
| ((unsigned)src[i*3+0] << 16)
|
||||
| ((unsigned)src[i*3+1] << 8)
|
||||
| ((unsigned)src[i*3+2] );
|
||||
data[i + 1] = 0xFF000000u
|
||||
| ((unsigned)src[i*3+3] << 16)
|
||||
| ((unsigned)src[i*3+4] << 8)
|
||||
| ((unsigned)src[i*3+5] );
|
||||
data[i + 2] = 0xFF000000u
|
||||
| ((unsigned)src[i*3+6] << 16)
|
||||
| ((unsigned)src[i*3+7] << 8)
|
||||
| ((unsigned)src[i*3+8] );
|
||||
data[i + 3] = 0xFF000000u
|
||||
| ((unsigned)src[i*3+9] << 16)
|
||||
| ((unsigned)src[i*3+10] << 8)
|
||||
| ((unsigned)src[i*3+11] );
|
||||
}
|
||||
for (; i < width; i++)
|
||||
data[i] = 0xFF000000u
|
||||
| ((unsigned)src[i*3+0] << 16)
|
||||
@@ -493,7 +489,10 @@ static void rpng_copy_line_rgb_sse2(uint32_t *data,
|
||||
/* Pack 8-bit RGBA bytes into ARGB32 or ABGR32 words.
|
||||
* Each input pixel is 4 bytes: R G B A
|
||||
* ARGB output: (A<<24)|(R<<16)|(G<<8)|B
|
||||
* ABGR output: (A<<24)|(B<<16)|(G<<8)|R (when supports_rgba) */
|
||||
* ABGR output: (A<<24)|(B<<16)|(G<<8)|R (when supports_rgba)
|
||||
* On LE (implied by SSE2/x86) the ABGR layout is the input bytes
|
||||
* verbatim, so that case is a straight row copy; the ARGB layout is a
|
||||
* vectorized R/B swap within each word. */
|
||||
#if defined(RPNG_SIMD_SSE2)
|
||||
static void rpng_copy_line_rgba_sse2(uint32_t *data,
|
||||
const uint8_t *src, unsigned width, bool supports_rgba)
|
||||
@@ -501,38 +500,24 @@ static void rpng_copy_line_rgba_sse2(uint32_t *data,
|
||||
unsigned i = 0;
|
||||
if (supports_rgba)
|
||||
{
|
||||
for (; (int)(width - i) >= 4; i += 4)
|
||||
{
|
||||
data[i+0] = ((unsigned)src[i*4+3] << 24) | ((unsigned)src[i*4+2] << 16)
|
||||
| ((unsigned)src[i*4+1] << 8) | ((unsigned)src[i*4+0]);
|
||||
data[i+1] = ((unsigned)src[i*4+7] << 24) | ((unsigned)src[i*4+6] << 16)
|
||||
| ((unsigned)src[i*4+5] << 8) | ((unsigned)src[i*4+4]);
|
||||
data[i+2] = ((unsigned)src[i*4+11] << 24) | ((unsigned)src[i*4+10] << 16)
|
||||
| ((unsigned)src[i*4+9] << 8) | ((unsigned)src[i*4+8]);
|
||||
data[i+3] = ((unsigned)src[i*4+15] << 24) | ((unsigned)src[i*4+14] << 16)
|
||||
| ((unsigned)src[i*4+13] << 8) | ((unsigned)src[i*4+12]);
|
||||
}
|
||||
for (; i < width; i++)
|
||||
data[i] = ((unsigned)src[i*4+3] << 24) | ((unsigned)src[i*4+2] << 16)
|
||||
| ((unsigned)src[i*4+1] << 8) | ((unsigned)src[i*4+0]);
|
||||
memcpy(data, src, (size_t)width * 4);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
const __m128i keep = _mm_set1_epi32((int)0xFF00FF00u);
|
||||
const __m128i lowm = _mm_set1_epi32(0xFF);
|
||||
for (; (int)(width - i) >= 4; i += 4)
|
||||
{
|
||||
data[i+0] = ((unsigned)src[i*4+3] << 24) | ((unsigned)src[i*4+0] << 16)
|
||||
| ((unsigned)src[i*4+1] << 8) | ((unsigned)src[i*4+2]);
|
||||
data[i+1] = ((unsigned)src[i*4+7] << 24) | ((unsigned)src[i*4+4] << 16)
|
||||
| ((unsigned)src[i*4+5] << 8) | ((unsigned)src[i*4+6]);
|
||||
data[i+2] = ((unsigned)src[i*4+11] << 24) | ((unsigned)src[i*4+8] << 16)
|
||||
| ((unsigned)src[i*4+9] << 8) | ((unsigned)src[i*4+10]);
|
||||
data[i+3] = ((unsigned)src[i*4+15] << 24) | ((unsigned)src[i*4+12] << 16)
|
||||
| ((unsigned)src[i*4+13] << 8) | ((unsigned)src[i*4+14]);
|
||||
__m128i w = _mm_loadu_si128((const __m128i*)(src + (size_t)i * 4));
|
||||
__m128i lo = _mm_slli_epi32(_mm_and_si128(w, lowm), 16);
|
||||
__m128i hi = _mm_and_si128(_mm_srli_epi32(w, 16), lowm);
|
||||
_mm_storeu_si128((__m128i*)(data + i),
|
||||
_mm_or_si128(_mm_and_si128(w, keep), _mm_or_si128(lo, hi)));
|
||||
}
|
||||
for (; i < width; i++)
|
||||
data[i] = ((unsigned)src[i*4+3] << 24) | ((unsigned)src[i*4+0] << 16)
|
||||
| ((unsigned)src[i*4+1] << 8) | ((unsigned)src[i*4+2]);
|
||||
}
|
||||
for (; i < width; i++)
|
||||
data[i] = ((unsigned)src[i*4+3] << 24) | ((unsigned)src[i*4+0] << 16)
|
||||
| ((unsigned)src[i*4+1] << 8) | ((unsigned)src[i*4+2]);
|
||||
}
|
||||
#endif /* RPNG_SIMD_SSE2 */
|
||||
|
||||
|
||||
Reference in new issue
Block a user