Быстрое векторизованное преобразование из RGB в BGRA - PullRequest
6 голосов
/ 25 августа 2011

В продолжение некоторых предыдущих вопросов о преобразовании RGB в RGBA и ARGB в BGR я хотел бы ускорить преобразование RGB в BGRA с SSE . Предположим, что 32-битный компьютер, и хотел бы использовать intrinsics . У меня возникают трудности с выравниванием исходного и целевого буферов для работы с 128-битными регистрами и поиска других подкованных решений векторизации.

Процедура векторизации выглядит следующим образом ...

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

Эта процедура в основном используется для больших текстур (512 КБ), поэтому, если я смогу распараллелить некоторые операции, может быть полезно обрабатывать больше пикселей за один раз. Конечно, мне нужно в профиль. :)

Edit:

Мои аргументы компиляции ...

gcc -O2 main.c

Ответы [ 4 ]

9 голосов
/ 27 августа 2011

Это пример использования встроенных функций SSSE3 для выполнения запрошенной операции. Указатели ввода и вывода должны быть выровнены по 16 байтов, и он работает с блоком из 16 пикселей за раз.

#include <tmmintrin.h>

/* 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;
    }
}
2 голосов
/ 10 июня 2014

Ммм ... использование vImageConvert_RGB888toARGB8888 ОЧЕНЬ ОЧЕНЬ быстро (15-кратное ускорение).

Выше кода PixelFix (≈6 мс на изображение, теперь на новом оборудовании)


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

Использование vImageConvert_RGB888toARGB888, с резьбой (на более новом оборудовании)


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

Должен ли я сказать больше?

2 голосов
/ 26 августа 2013

Я лично обнаружил, что реализация следующего дала мне лучший результат для преобразования BGR-24 в ARGB-32.

Этот код работает на скорости около 8,8 мс на изображении, тогда как представленный выше 128-битный код векторизации имеет скорость 14,5 мс на изображение.

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<h; j++) {
        for (i=0; i<w; i++) {
            buff[picptr++]=(diskmem[srcptr]<<24) | (diskmem[srcptr+1]<<16) | diskmem[srcptr+2]<<8 | 0xff;
            srcptr+=3;
        }
    }
}

Раньше я использовал эту процедуру (около 13,2 мс на изображение). Здесь бафф - это беззнаковый символ *.

for (j=0; j<h; j++) {
    int srcptr = (h-j-1)*w*3;  // remove if you don't want vertical flipping
    for (i=0; i<w; i++) {
        buff[picptr+3]=diskmem[srcptr++]; // b
        buff[picptr+2]=diskmem[srcptr++]; // g
        buff[picptr+1]=diskmem[srcptr++]; // r
        buff[picptr+0]=255;               // a
        picptr+=4;
    }
}

Запуск MacMini 2,6 ГГц / i7 2012 года.

2 голосов
/ 25 августа 2011

У меня нет полного понимания того, что вы просите, и я с нетерпением жду правильного ответа на ваш вопрос. Тем временем я пришел к выводу, что реализация в среднем примерно на 8-10% быстрее. Я использую Win7 64bit, использую VS2010, компилирую с C ++ для выпуска с опцией fast.

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

Это может или не может помочь, но я надеюсь, что это так. Пожалуйста, не голосуйте за меня, если это не так, я просто пытаюсь продвинуть это.

Моя мотивация для использования структур - позволить компилятору максимально эффективно продвигать указатели src и dst. Другой мотивацией является ограничение количества арифметических операций.

...