2012-06-13 21 views
6

Sto implementando una funzione di conversione veloce x888 -> 565 pixel in pixman in base all'algoritmo descritto by Intel [pdf]. Il loro codice converte x888 -> 555 mentre voglio convertire in 565. Sfortunatamente, la conversione in 565 significa che il bit più alto è impostato, il che significa che non posso usare le istruzioni del pacchetto di saturazione firmata. L'istruzione pack senza firma, packusdw non è stata aggiunta fino a SSE4.1. Mi piacerebbe implementare la sua funzionalità con SSE2 o trovare un altro modo per farlo.Simulazione della funzionalità packusdw con SSE2

Questa funzione richiede due registri XMM contenenti 4 pixel a 32 bit ciascuno e genera un singolo registro XMM contenente gli 8 pixel RGB565 convertiti.

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

idee che ho pensato di:

  • Sottraendo 0x8000, _mm_packs_epi32, ri-aggiungendo 0x8000 ad ogni 565 pixel. Ho provato questo, ma non posso farlo funzionare.

    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); 
    
  • Mischiare i dati invece di imballarli. Funziona per MMX, ma dal momento che gli shuffles SSE a 16 bit funzionano solo sui 64 bit alti o bassi, sarebbe un po 'complicato.

  • Salvare i bit alti, impostarli su zero, eseguire il pacchetto, ripristinarli in seguito. Sembra abbastanza disordinato.

C'è qualche altro modo (spero più efficiente) di farlo?

risposta

5

Si potrebbe firmare estendere i valori prima e quindi utilizzare _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); 

Si potrebbe effettivamente combinare questo con i turni precedenti per salvare due istruzioni:

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

Perfetto! Molte grazie. Dubito che possa essere fatto in modo più efficiente. – mattst88