support using inline asm for cpuid

YCbCr:
  switch SSE code to constants that match old C;
  create C version that is same as SSE;
  tiny optimization(?) of SSE
This commit is contained in:
Sean Barrett 2014-12-19 04:39:04 -08:00
parent b082091bcb
commit f259bf27e9
2 changed files with 114 additions and 24 deletions

View File

@ -81,7 +81,8 @@
// Limitations:
// - no jpeg progressive support
// - non-HDR formats support 8-bit samples only (jpeg, png)
// - no 16-bit-per-channel PNG
// - no 12-bit-per-channel jpeg
// - no delayed line count (jpeg) -- IJG doesn't support either
// - no 1-bit BMP
// - GIF always returns *comp=4
@ -196,8 +197,12 @@
//
// SIMD support
//
// The JPEG decoder will automatically use SIMD kernels where supported,
// replacing the STBI_SIMD-do-it-yourself interface from previous versions.
// The JPEG decoder will automatically use SIMD kernels on x86 platforms
// where supported.
//
// (The old do-it-yourself SIMD API is no longer supported in the current
// code.)
//
// The code will automatically detect if the required SIMD instructions are
// available, and fall back to the generic C version where they're not.
//
@ -402,16 +407,35 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
#include <emmintrin.h>
#ifdef _MSC_VER
#if _MSC_VER >= 1400 // not VC6
#include <intrin.h> // __cpuid
static int stbi__cpuid3(void)
{
int info[4];
__cpuid(info,1);
return info[3];
}
#else
static int stbi__cpuid3(void)
{
int res;
__asm {
mov eax,1
cpuid
mov res,edx
}
return res;
}
#endif
#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name
static int stbi__sse2_available()
{
int info[4];
__cpuid(info, 1);
return ((info[3] >> 26) & 1) != 0;
int info3 = stbi__cpuid3();
return ((info3 >> 26) & 1) != 0;
}
#else // assume GCC-style if not VC++
#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
@ -2117,6 +2141,35 @@ static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc
}
}
#define float2fixed2(x) (((int) ((x) * 4096.0f + 0.5f)) << 8)
static void stbi__YCbCr_to_RGB_backport(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step)
{
int i;
for (i=0; i < count; ++i) {
int y_fixed = (y[i] << 20) + (1<<19); // rounding
int r,g,b;
int cr = pcr[i] - 128;
int cb = pcb[i] - 128;
r = y_fixed + cr*float2fixed2(1.40200f);
g = y_fixed;
g += (cr*-float2fixed2(0.71414f)) & 0xffff0000;
g += (cb*-float2fixed2(0.34414f)) & 0xffff0000;
b = y_fixed + cb*float2fixed2(1.77200f);
r >>= 20;
g >>= 20;
b >>= 20;
if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; }
if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; }
if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; }
out[0] = (stbi_uc)r;
out[1] = (stbi_uc)g;
out[2] = (stbi_uc)b;
out[3] = 255;
out += step;
}
}
#ifdef STBI_SSE2
static void stbi__YCbCr_to_RGB_sse2(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step)
{
@ -2130,37 +2183,35 @@ static void stbi__YCbCr_to_RGB_sse2(stbi_uc *out, stbi_uc const *y, stbi_uc cons
if (step == 4) {
// this is a fairly straightforward implementation and not super-optimized.
__m128i signflip = _mm_set1_epi8(-0x80);
__m128i cr_const0 = _mm_set1_epi16((short) ( 1.40200f*4096.0f));
__m128i cr_const1 = _mm_set1_epi16((short) (-0.71414f*4096.0f));
__m128i cb_const0 = _mm_set1_epi16((short) (-0.34414f*4096.0f));
__m128i cb_const1 = _mm_set1_epi16((short) ( 1.77200f*4096.0f));
__m128i y_bias = _mm_set1_epi16(8);
__m128i xw = _mm_set1_epi16(255);
__m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f));
__m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f));
__m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f));
__m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f));
__m128i y_bias = _mm_set1_epi8((char) 128);
__m128i xw = _mm_set1_epi16(255); // alpha channel
for (; i+7 < count; i += 8) {
// load
__m128i zero = _mm_setzero_si128();
__m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i));
__m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i));
__m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i));
__m128i cr_bias = _mm_xor_si128(cr_bytes, signflip); // -128
__m128i cb_bias = _mm_xor_si128(cb_bytes, signflip); // -128
__m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128
__m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128
// unpack to short (and left-shift cr, cb by 8)
__m128i yw = _mm_unpacklo_epi8(y_bytes, zero);
__m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_bias);
__m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_bias);
__m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes);
__m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased);
__m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased);
// color transform
__m128i yws = _mm_slli_epi16(yw, 4);
__m128i yws = _mm_srli_epi16(yw, 4);
__m128i cr0 = _mm_mulhi_epi16(cr_const0, crw);
__m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw);
__m128i ywb = _mm_add_epi16(yws, y_bias);
__m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1);
__m128i cr1 = _mm_mulhi_epi16(crw, cr_const1);
__m128i rws = _mm_add_epi16(cr0, ywb);
__m128i gwt = _mm_add_epi16(cb0, ywb);
__m128i bws = _mm_add_epi16(ywb, cb1);
__m128i rws = _mm_add_epi16(cr0, yws);
__m128i gwt = _mm_add_epi16(cb0, yws);
__m128i bws = _mm_add_epi16(yws, cb1);
__m128i gws = _mm_add_epi16(gwt, cr1);
// descale

View File

@ -9,9 +9,48 @@
#define PNGSUITE_PRIMARY
#if 1
void test_ycbcr(void)
{
STBI_SIMD_ALIGN(unsigned char, y[256]);
STBI_SIMD_ALIGN(unsigned char, cb[256]);
STBI_SIMD_ALIGN(unsigned char, cr[256]);
STBI_SIMD_ALIGN(unsigned char, out1[256][4]);
STBI_SIMD_ALIGN(unsigned char, out2[256][4]);
int i,j,k;
int count = 0, bigcount=0;
for (i=0; i < 256; ++i) {
for (j=0; j < 256; ++j) {
for (k=0; k < 256; ++k) {
y[k] = k;
cb[k] = j;
cr[k] = i;
}
stbi__YCbCr_to_RGB_sse2(out1[0], y, cb, cr, 256, 4);
stbi__YCbCr_to_RGB_backport(out2[0], y, cb, cr, 256, 4);
for (k=0; k < 256; ++k) {
if (out1[k][0] != out2[k][0] || out1[k][1] != out2[k][1] || out1[k][2] != out2[k][2]) {
int dist1 = abs(out1[k][0] - out2[k][0]);
int dist2 = abs(out1[k][1] - out2[k][1]);
int dist3 = abs(out1[k][2] - out2[k][2]);
++count;
if (dist2)
++bigcount;
}
}
}
printf("So far: %d (%d big)\n", count, bigcount);
}
printf("Final: %d (%d big)\n", count, bigcount);
}
#endif
int main(int argc, char **argv)
{
int w,h;
test_ycbcr();
if (argc > 1) {
int i;
for (i=1; i < argc; ++i) {