2012-06-13 17 views
6

Ich implementiere eine schnelle x888 -> 565-Pixel-Konvertierungsfunktion in pixman nach dem beschriebenen Algorithmus by Intel [pdf]. Ihr Code konvertiert x888 -> 555, während ich in 565 konvertieren möchte. Leider bedeutet die Konvertierung in 565, dass das High-Bit gesetzt ist, was bedeutet, dass ich keine Anweisungen für signiertes Sättigungspaket verwenden kann. Die unsignierte Pack-Anweisung, packusdw, wurde erst in SSE 4.1 hinzugefügt. Ich möchte seine Funktionalität mit SSE2 implementieren oder eine andere Möglichkeit finden, dies zu tun.Simulation der packusdw-Funktionalität mit SSE2

Diese Funktion benötigt zwei XMM-Register, die jeweils 4 32-Bit-Pixel enthalten, und gibt ein einzelnes XMM-Register aus, das die 8 konvertierten RGB565-Pixel enthält.

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

Ideen ich gedacht habe:

  • Subtrahierend 0x8000, _mm_packs_epi32, re schöpf 0x8000 zu jedem 565 Pixel. Ich habe das versucht, aber ich kann das nicht schaffen.

    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); 
    
  • Daten mischen statt packen. Funktioniert für MMX, aber da SSE-16-Bit-Shuffles nur mit den hohen oder niedrigen 64-Bit arbeiten, würde es unordentlich werden.

  • Speichern Sie hohe Bits, setzen Sie sie auf Null, machen Sie das Paket, stellen Sie sie danach wieder her. Scheint ziemlich chaotisch.

Gibt es noch andere (hoffentlich effizientere) Möglichkeiten, dies zu tun?

Antwort

5

Sie unterzeichnen könnten die Werte zuerst erweitern und dann _mm_packs_epi32 verwenden:

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

du tatsächlich könnte die vorangegangenen Schichten verbinden sich mit zwei Anweisungen zu speichern:

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! Danke vielmals. Ich bezweifle, dass es effizienter gemacht werden kann. – mattst88