2012-06-13 6 views
6

by Intel [pdf]에 설명 된 알고리즘에 따라 pixman에 빠른 x888 -> 565 픽셀 변환 기능을 구현하고 있습니다. 그들의 코드는 565로 변환하고 싶을 때 x888 -> 555로 변환합니다. 불행히도 565로 변환한다는 것은 높은 비트가 설정된다는 것을 의미합니다. 이는 부호가있는 채도 팩 명령어를 사용할 수 없음을 의미합니다. 부호없는 팩 명령어 인 packusdw는 SSE4.1까지 추가되지 않았습니다. SSE2로 기능을 구현하거나 다른 방법을 찾고 싶습니다.SSE2로 packusdw 기능 시뮬레이션

이 함수는 4 개의 32 비트 픽셀을 각각 포함하는 2 개의 XMM 레지스터를 사용하고 8 개의 변환 된 RGB565 픽셀을 포함하는 단일 XMM 레지스터를 출력합니다. 내가 생각했습니다

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); 
} 

아이디어 :

  • 은 빼면 0x8000이, _mm_packs_epi32, 다시 추가 각 565 픽셀에 0x8000을합니다. 나는 이것을 시도했지만, 나는이 일을 할 수 없다.

    t0 = _mm_sub_epi16 (t0, mask_8000); 
    t1 = _mm_sub_epi16 (t1, mask_8000); 
    t0 = _mm_packs_epi32 (t0, t1); 
    return _mm_add_epi16 (t0, mask_8000); 
    
  • 데이터를 패킹하는 대신에 셔플하십시오. MMX에서 작동하지만 SSE 16 비트 셔플은 높거나 낮은 64 비트에서만 작동하므로 지저분 해집니다.

  • 상위 비트를 저장하고 0으로 설정 한 후 팩을 수행 한 다음 나중에 복원하십시오. 꽤 지저분 해 보입니다.

내가 할 수있는 다른 (잘하면 더 효율적인) 방법이 있습니까?

답변

5

당신은 _mm_packs_epi32을 먼저 값을 확장 한 후 사용 등록 수 :

t0 = _mm_slli_epi32 (t0, 16); 
t0 = _mm_srai_epi32 (t0, 16); 
t1 = _mm_slli_epi32 (t1, 16); 
t1 = _mm_srai_epi32 (t1, 16); 
t0 = _mm_packs_epi32 (t0, t1); 

당신은 실제로 두 개의 명령어를 저장하기 위해 이전의 변화와 함께이 결합 수 :

t0 = _mm_slli_epi32 (t0, 16 - 5); 
t0 = _mm_srai_epi32 (t0, 16); 
t1 = _mm_slli_epi32 (t1, 16 - 5); 
t1 = _mm_srai_epi32 (t1, 16); 
t0 = _mm_packs_epi32 (t0, t1); 
+1

완벽! 고마워. 나는 그것이 더 효율적으로 수행 될 수 있을지 의심 스럽다. – mattst88