2012-06-13 14 views
6

Estoy implementando una función rápida de conversión x888 -> 565 píxeles en pixman de acuerdo con el algoritmo descrito by Intel [pdf]. Su código convierte x888 -> 555 mientras que yo quiero convertirlo a 565. Lamentablemente, convertir a 565 significa que el bit alto está configurado, lo que significa que no puedo usar las instrucciones del paquete de saturación firmada. La instrucción de paquete sin signo, packusdw no se agregó hasta SSE4.1. Me gustaría implementar su funcionalidad con SSE2 o encontrar otra forma de hacerlo.Simulando la funcionalidad packusdw con SSE2

Esta función toma dos registros XMM que contienen 4 píxeles de 32 bits cada uno y genera un solo registro XMM que contiene los 8 píxeles convertidos 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); 
} 

ideas que he pensado:

  • Resta de 0x8000, _mm_packs_epi32, volver a agregar a cada 0x8000 565 píxeles. Intenté esto, pero no puedo hacer que esto funcione.

    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); 
    
  • Mezcle los datos en lugar de empaquetarlo. Funciona para MMX, pero como la mezcla SSE de 16 bits funciona solo en los 64 bits altos o bajos, se complicaría.

  • Guarde los bits altos, póngalos a cero, haga el paquete, restáurelos después. Parece bastante desordenado.

¿Hay alguna otra (ojalá más eficiente) forma en que pueda hacer esto?

Respuesta

5

Se podría firmar extender los valores primero y luego usar _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); 

En realidad podría combinar esto con los cambios anteriores para salvar dos instrucciones:

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

perfecto! Muchas gracias. Dudo que se pueda hacer de manera más eficiente. – mattst88