Conversion vectorisée rapide de RVB à BGRA

Pour faire suite à des questions précédentes sur la conversion de RVB en RGBA et d’ARGB en BGR, j’aimerais accélérer la conversion de RVB à BGRA avec SSE . Supposons une machine 32 bits et voudrions utiliser des éléments insortingnsèques . Je ne parviens pas à aligner les tampons source et de destination sur des registres 128 bits et à rechercher d’autres solutions de vectorisation intelligentes.

La routine à vectoriser est la suivante …

void RGB8ToBGRX8(int w, const void *in, void *out) { int i; int width = w; const unsigned char *src= (const unsigned char*) in; unsigned int *dst= (unsigned int*) out; unsigned int invalue, outvalue; for (i=0; i<width; i++, src+=3, dst++) { invalue = src[0]; outvalue = (invalue<<16); invalue = src[1]; outvalue |= (invalue<<8); invalue = src[2]; outvalue |= (invalue); *dst = outvalue | 0xff000000; } } 

Cette routine est principalement utilisée pour les grandes textures (512 Ko). Par conséquent, si je peux paralléliser certaines des opérations, il peut être intéressant de traiter plus de pixels en une fois. Bien sûr, je vais avoir besoin de profiler. 🙂

Modifier:

Mes arguments de compilation …

 gcc -O2 main.c 

Ceci est un exemple d’utilisation d’insortingnsèques SSSE3 pour effectuer l’opération demandée. Les pointeurs d’entrée et de sortie doivent être alignés sur 16 octets et opèrent par bloc de 16 pixels à la fois.

 #include  /* in and out must be 16-byte aligned */ void rgb_to_bgrx_sse(unsigned w, const void *in, void *out) { const __m128i *in_vec = in; __m128i *out_vec = out; w /= 16; while (w-- > 0) { /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 * in_vec[0] Ra Ga Ba Rb Gb Bb Rc Gc Bc Rd Gd Bd Re Ge Be Rf * in_vec[1] Gf Bf Rg Gg Bg Rh Gh Bh Ri Gi Bi Rj Gj Bj Rk Gk * in_vec[2] Bk Rl Gl Bl Rm Gm Bm Rn Gn Bn Ro Go Bo Rp Gp Bp */ __m128i in1, in2, in3; __m128i out; in1 = in_vec[0]; out = _mm_shuffle_epi8(in1, _mm_set_epi8(0xff, 9, 10, 11, 0xff, 6, 7, 8, 0xff, 3, 4, 5, 0xff, 0, 1, 2)); out = _mm_or_si128(out, _mm_set_epi8(0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0)); out_vec[0] = out; in2 = in_vec[1]; in1 = _mm_and_si128(in1, _mm_set_epi8(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0)); out = _mm_and_si128(in2, _mm_set_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff)); out = _mm_or_si128(out, in1); out = _mm_shuffle_epi8(out, _mm_set_epi8(0xff, 5, 6, 7, 0xff, 2, 3, 4, 0xff, 15, 0, 1, 0xff, 12, 13, 14)); out = _mm_or_si128(out, _mm_set_epi8(0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0)); out_vec[1] = out; in3 = in_vec[2]; in_vec += 3; in2 = _mm_and_si128(in2, _mm_set_epi8(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0)); out = _mm_and_si128(in3, _mm_set_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff)); out = _mm_or_si128(out, in2); out = _mm_shuffle_epi8(out, _mm_set_epi8(0xff, 1, 2, 3, 0xff, 14, 15, 0, 0xff, 11, 12, 13, 0xff, 8, 9, 10)); out = _mm_or_si128(out, _mm_set_epi8(0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0)); out_vec[2] = out; out = _mm_shuffle_epi8(in3, _mm_set_epi8(0xff, 13, 14, 15, 0xff, 10, 11, 12, 0xff, 7, 8, 9, 0xff, 4, 5, 6)); out = _mm_or_si128(out, _mm_set_epi8(0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0, 0xff, 0, 0, 0)); out_vec[3] = out; out_vec += 4; } } 

Je ne comprends pas tout à fait ce que vous demandez et j’attends avec impatience une réponse appropriée à votre question. Entre-temps, je suis arrivé à une mise en œuvre plus rapide de 8 à 10% en moyenne. J’exécute Win7 64 bits, en utilisant VS2010, en compilant avec C ++ pour la publication avec l’option rapide.

 #pragma pack(push, 1) struct RGB { unsigned char r, g, b; }; struct BGRA { unsigned char b, g, r, a; }; #pragma pack(pop) void RGB8ToBGRX8(int width, const void* in, void* out) { const RGB* src = (const RGB*)in; BGRA* dst = (BGRA*)out; do { dst->r = src->r; dst->g = src->g; dst->b = src->b; dst->a = 0xFF; src++; dst++; } while (--width); } 

Cela peut ou peut ne pas aider, mais j’espère que cela aidera. S’il vous plaît, ne votez pas contre moi si ce n’est pas le cas, j’essaie simplement de faire avancer les choses.

Ma motivation pour utiliser des structures est de permettre au compilateur de faire avancer le plus efficacement possible les pointeurs src et dst. Une autre motivation est de limiter le nombre d’opérations arithmétiques.

J’ai personnellement constaté que la mise en œuvre de ce qui suit m’a permis d’obtenir le meilleur résultat pour la conversion de BGR-24 en ARGB-32.

Ce code fonctionne à environ 8,8 ms sur une image alors que le code de vectorisation à 128 bits présenté ci-dessus était de 14,5 ms par image.

 void PixelFix(u_int32_t *buff,unsigned char *diskmem) { int i,j; int picptr, srcptr; int w = 1920; int h = 1080; for (j=0; j 

Auparavant, j'utilisais cette routine (environ 13,2 ms par image). Ici, buff est un caractère non signé *.

 for (j=0; j 

Exécution d’un MacMini 2.6ghz / i7 2012.

Ummm … utiliser vImageConvert_RGB888toARGB8888 est très rapide (15 fois plus rapide).

Code PixelFix ci-dessus (ms6 ms par image, désormais sur du matériel plus récent)


  1. 6,373520 ms
  2. 6,383363 ms
  3. 6,413560 ms
  4. 6,278606 ms
  5. 6,293607 ms
  6. 6,368118 ms
  7. 6,338904 ms
  8. 6,389385 ms
  9. 6,365495 ms

Utilisation de vImageConvert_RGB888toARGB888, fileté (sur du matériel plus récent)


  1. 0,563649 ms
  2. 0,400387 ms
  3. 0,375198 ms
  4. 0,360898 ms
  5. 0,391278 ms
  6. 0,396797 ms
  7. 0,405534 ms
  8. 0,386495 ms
  9. 0,367621 ms

Ai-je besoin d’en dire plus?