2011-05-11 2 views
7

내장 함수 사용에 익숙해지기 위해 한 번에 필터링 된 샘플 하나에 대해 합리적인 속도의 이중 선형 필터링 함수를 계산하려고합니다. 최대 SSE41까지 사용할 수 있습니다.SSE4.1 내장 함수가있는 이중 선형 필터

inline __m128i DivideBy255_8xUint16(const __m128i value) 
{ 
    // Blinn 16bit divide by 255 trick but across 8 packed 16bit values 
    const __m128i plus128 = _mm_add_epi16(value, _mm_set1_epi16(128)); 
    const __m128i plus128ThenDivideBy256 = _mm_srli_epi16(plus128, 8);   // TODO: Should this be an arithmetic or logical shift or does it matter? 
    const __m128i partial = _mm_add_epi16(plus128, plus128ThenDivideBy256); 
    const __m128i result = _mm_srli_epi16(partial, 8);       // TODO: Should this be an arithmetic or logical shift or does it matter? 


    return result; 
} 


inline uint32_t BilinearSSE41(const uint8_t* data, uint32_t pitch, uint32_t width, uint32_t height, float u, float v) 
{ 
    // TODO: There are probably intrinsics I haven't found yet to avoid using these? 
    // 0x80 is high bit set which means zero out that component 
    const __m128i unpack_fraction_u_mask = _mm_set_epi8(0x80, 0, 0x80, 0, 0x80, 0, 0x80, 0, 0x80, 0, 0x80, 0, 0x80, 0, 0x80, 0); 
    const __m128i unpack_fraction_v_mask = _mm_set_epi8(0x80, 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 1); 
    const __m128i unpack_two_texels_mask = _mm_set_epi8(0x80, 7, 0x80, 6, 0x80, 5, 0x80, 4, 0x80, 3, 0x80, 2, 0x80, 1, 0x80, 0); 


    // TODO: Potentially wasting two channels of operations for now 
    const __m128i size = _mm_set_epi32(0, 0, height - 1, width - 1); 
    const __m128 uv = _mm_set_ps(0.0f, 0.0f, v, u); 

    const __m128 floor_uv_f = _mm_floor_ps(uv); 
    const __m128 fraction_uv_f = _mm_sub_ps(uv, floor_uv_f); 
    const __m128 fraction255_uv_f = _mm_mul_ps(fraction_uv_f, _mm_set_ps1(255.0f)); 
    const __m128i fraction255_uv_i = _mm_cvttps_epi32(fraction255_uv_f); // TODO: Did this get rounded correctly? 

    const __m128i fraction255_u_i = _mm_shuffle_epi8(fraction255_uv_i, unpack_fraction_u_mask); // Splat fraction_u*255 across all 16 bit words 
    const __m128i fraction255_v_i = _mm_shuffle_epi8(fraction255_uv_i, unpack_fraction_v_mask); // Splat fraction_v*255 across all 16 bit words 

    const __m128i inverse_fraction255_u_i = _mm_sub_epi16(_mm_set1_epi16(255), fraction255_u_i); 
    const __m128i inverse_fraction255_v_i = _mm_sub_epi16(_mm_set1_epi16(255), fraction255_v_i); 

    const __m128i floor_uv_i = _mm_cvttps_epi32(floor_uv_f); 
    const __m128i clipped_floor_uv_i = _mm_min_epu32(floor_uv_i, size); // TODO: I haven't clamped this probably if uv was less than zero yet... 


    // TODO: Calculating the addresses in the SSE register set would maybe be better 

    int u0 = _mm_extract_epi32(floor_uv_i, 0); 
    int v0 = _mm_extract_epi32(floor_uv_i, 1); 


    const uint8_t* row = data + (u0<<2) + pitch*v0; 


    const __m128i row0_packed = _mm_loadl_epi64((const __m128i*)data); 
    const __m128i row0 = _mm_shuffle_epi8(row0_packed, unpack_two_texels_mask); 

    const __m128i row1_packed = _mm_loadl_epi64((const __m128i*)(data + pitch)); 
    const __m128i row1 = _mm_shuffle_epi8(row1_packed, unpack_two_texels_mask); 


    // Compute (row0*fraction)/255 + row1*(255 - fraction)/255 - probably slight precision loss across addition! 
    const __m128i vlerp0 = DivideBy255_8xUint16(_mm_mullo_epi16(row0, fraction255_v_i)); 
    const __m128i vlerp1 = DivideBy255_8xUint16(_mm_mullo_epi16(row1, inverse_fraction255_v_i)); 
    const __m128i vlerp = _mm_adds_epi16(vlerp0, vlerp1); 

    const __m128i hlerp0 = DivideBy255_8xUint16(_mm_mullo_epi16(vlerp, fraction255_u_i)); 
    const __m128i hlerp1 = DivideBy255_8xUint16(_mm_srli_si128(_mm_mullo_epi16(vlerp, inverse_fraction255_u_i), 16 - 2*4)); 
    const __m128i hlerp = _mm_adds_epi16(hlerp0, hlerp1); 


    // Pack down to 8bit from 16bit components and return 32bit ARGB result 
    return _mm_extract_epi32(_mm_packus_epi16(hlerp, hlerp), 0); 
} 

코드는 이미지 데이터가 렌더링 ARGB8하고 분기로하지 않고 모서리 케이스를 처리하기 위해 별도의 열과 행이 가정

지금까지 나는 다음 있습니다.

나는이 글의 엉망진창의 크기를 줄이기 위해 사용할 수있는 지침에 대해 조언 한 후 물론 더 빨리 실행되도록 개선 할 수 있습니다.

고마워요 :)

답변

1

귀하의 코드에 대해 특별한 언급이 없습니다. SSE2를 사용하여 Bilinear 스케일링 코드를 작성했습니다. 자세한 내용은 StackOverflow 질문 Help me improve some more SSE2 code을 참조하십시오.

제 코드에서는 픽셀 단위가 아닌 가로 및 세로 비율과 인덱스를 먼저 계산합니다. 나는 이것이 더 빠르다고 생각한다.

내 코드가 core2 인 것보다 CPU가 메모리가 부족하여 precalc를 더 빨리 수행하지 못하는 것 같습니다.

1

"TODO : 이것은 산술 또는 논리 이동이어야합니까, 아니면 중요합니까?"

부호 변경은 부호있는 정수를 나타냅니다. 논리적 시프트는 부호없는 정수입니다.

0x80000000 >> 4 is 0xf8000000 // Arithmetic shift 
    0x80000000 >> 4 is 0x08000000 // Logical shift