2012-06-13 14 views
6

Wprowadzam szybką funkcję konwersji x888 -> 565 pikseli w pixman zgodnie z algorytmem opisanym jako by Intel [pdf]. Ich kod konwertuje x888 -> 555, podczas gdy ja chcę przekonwertować na 565. Niestety, konwersja do 565 oznacza, że ​​ustawiony jest wysoki bit, co oznacza, że ​​nie mogę używać instrukcji zestawu sygnatur nasycenia. Niepodpisana instrukcja packus packwd została dodana dopiero w wersji SSE4.1. Chciałbym wdrożyć jego funkcjonalność za pomocą SSE2 lub znaleźć inny sposób na to.Symulowanie funkcjonalności packusdw z SSE2

Ta funkcja pobiera dwa rejestry XMM zawierające 4 32-bitowe piksele i wyprowadza pojedynczy rejestr XMM zawierający 8 przekonwertowanych pikseli RGB565.

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

Pomysły Myślałem o:

  • odjęcie 0x8000, _mm_packs_epi32, ponowne dodanie do każdego 0x8000 565 pikseli. Próbowałem tego, ale nie mogę tego zrobić.

    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); 
    
  • Przetasuj dane zamiast je pakować. Działa dla MMX, ale odkąd 16-bitowe tasowanie SSE działa tylko na 64-bitowych high lub low, byłby brudny.

  • Zapisz wysokie bity, ustaw je na zero, zrób pakiet, następnie przywróć je. Wydaje się dość niechlujny.

Czy jest jakiś inny (mam nadzieję, wydajniejszy) sposób, w jaki mógłbym to zrobić?

Odpowiedz

5

Można podpisać przedłużenie wartości, a następnie użyć _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); 

Można rzeczywiście połączyć to z wcześniejszymi zmianami, aby zapisać dwie instrukcje:

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

Perfect! Wielkie dzięki. Wątpię, by można to było zrobić bardziej efektywnie. – mattst88