Implementar una capacidad de CPU casi en tiempo real como Glalphafunc (GL_Greater) con la fuente RGB y la superposición de RGBA

StackOverflow https://stackoverflow.com//questions/22018826

Pregunta

La latencia es la mayor preocupación aquí. He encontrado que tratar de renderizar 3 fuentes de video de 1920x1080 con superposiciones de RGBA a Windows individuales a través de OpenGL tiene límites. Soy capaz de representar dos ventanas con superposiciones o 3 ventanas sin superposiciones, pero cuando se introduce la tercera ventana, los puestos de representación son obvios. Creo que el problema se debe al uso excesivo de Glalphafunc () a la superposición y la textura basada en RGBA en una textura de video RGB. Para reducir el uso excesivo, mi pensamiento es mover parte de la función de superposición en la CPU (ya que tengo un montón de CPU - Dual Hexcore Xeon). El lugar ideal para hacer esto sería cuando se copia la imagen RGB de origen al PBO asignado y reemplazando los valores RGB con los de la superposición de RGBA donde A> 0.

He intentado usar los métodos de IPP de Intel, pero no hay un método disponible que no involucre múltiples llamadas y resultados en demasiada latencia. He intentado el código recto de C, pero esto lleva más de las 33 ms que estoy permitido. Necesito ayuda para crear un ensamblaje optimizado o una rutina basada en SSE que proporcione una latencia mínima.

compile el código a continuación con> g ++ -fopenmp -o2 -mtune= nativo

Función básica C para mayor claridad:

void copyAndOverlay(const uint8_t* aSourceRGB, const uint8_t* aOverlayRGBA, uint8_t* aDestinationRGB, int aWidth, int aHeight) {
    int i;
#pragma omp parallel for
    for (i=0; i<aWidth*aHeight; ++i) {
        if (0 == aOverlayRGBA[i*4+3]) {
           aDestinationRGB[i*3] = aSourceRGB[i*3]; // R
           aDestinationRGB[i*3+1] = aSourceRGB[i*3+1]; // G
           aDestinationRGB[i*3+2] = aSourceRGB[i*3+2]; // B
        } else {
           aDestinationRGB[i*3] = aOverlayRGBA[i*4]; // R
           aDestinationRGB[i*3+1] = aOverlayRGBA[i*4+1]; // G
           aDestinationRGB[i*3+2] = aOverlayRGBA[i*4+2]; // B
        }
    }
}

uint64_t getTime() {
  struct timeval tNow;
  gettimeofday(&tNow, NULL);
  return (uint64_t)tNow.tv_sec * 1000000 + (uint64_t)tNow.tv_usec;
}

int main(int argc, char **argv) {
  int pixels = _WIDTH_ * _HEIGHT_ * 3;
  uint8_t *rgba = new uint8_t[_WIDTH_ * _HEIGHT_ * 4];
  uint8_t *src = new uint8_t[pixels];
  uint8_t *dst = new uint8_t[pixels];

  uint64_t tStart = getTime();

  for (int t=0; t<1000; ++t) {
    copyAndOverlay(src, rgba, dst, _WIDTH_, _HEIGHT_);
  }

  printf("delta: %lu\n", (getTime() - tStart) / 1000);

  delete [] rgba;
  delete [] src;
  delete [] dst; 

  return 0;
}

¿Fue útil?

Solución

Aquí hay una implementación SSE4 que es un poco más de 5 veces más rápido que el código que publicó con la pregunta (sin paralelización del bucle).Según lo escrito, solo funciona en los buffers de RGBA que son alineados y del tamaño de 16 bytes en múltiplos de 64, y en tampones RGB que son alineados y dimensionados de 16 bytes en múltiplos de 48. El tamaño requeridos se requieren perfectamente con su resolución de 1920x1080,Y es posible que deba agregar código para asegurarse de que sus búferes estén alineados por 16 bytes.

void copyAndOverlay(const uint8_t* aSourceRGB, const uint8_t* aOverlayRGBA, uint8_t* aDestinationRGB, int aWidth, int aHeight) {
  __m128i const ocmp     = _mm_setzero_si128();
  __m128i const omskshf1 = _mm_set_epi32(0x00000000, 0x0F0F0F0B, 0x0B0B0707, 0x07030303);
  __m128i const omskshf2 = _mm_set_epi32(0x07030303, 0x00000000, 0x0F0F0F0B, 0x0B0B0707);
  __m128i const omskshf3 = _mm_set_epi32(0x0B0B0707, 0x07030303, 0x00000000, 0x0F0F0F0B);
  __m128i const omskshf4 = _mm_set_epi32(0x0F0F0F0B, 0x0B0B0707, 0x07030303, 0x00000000);
  __m128i const ovalshf1 = _mm_set_epi32(0x00000000, 0x0E0D0C0A, 0x09080605, 0x04020100);
  __m128i const ovalshf2 = _mm_set_epi32(0x04020100, 0x00000000, 0x0E0D0C0A, 0x09080605);
  __m128i const ovalshf3 = _mm_set_epi32(0x09080605, 0x04020100, 0x00000000, 0x0E0D0C0A);
  __m128i const ovalshf4 = _mm_set_epi32(0x0E0D0C0A, 0x09080605, 0x04020100, 0x00000000);
  __m128i const blndmsk1 = _mm_set_epi32(0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000);
  __m128i const blndmsk2 = _mm_set_epi32(0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000);
  __m128i const blndmsk3 = _mm_set_epi32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000);
  __m128i       a, b, c, x, y, z, w, p, q, r, s;
  uint8_t const *const aSourceRGBPast = aSourceRGB + 3 * aWidth * aHeight;

  while (aSourceRGB != aSourceRGBPast) {
    // source:
    //  aaabbbcccdddeeef
    //  ffggghhhiiijjjkk
    //  klllmmmnnnoooppp
    //
    // overlay:
    //  aaaabbbbccccdddd
    //  eeeeffffgggghhhh
    //  iiiijjjjkkkkllll
    //  mmmmnnnnoooopppp

    // load source
    a = _mm_load_si128((__m128i const*)(aSourceRGB       ));
    b = _mm_load_si128((__m128i const*)(aSourceRGB   + 16));
    c = _mm_load_si128((__m128i const*)(aSourceRGB   + 32));

    // load overlay
    x = _mm_load_si128((__m128i const*)(aOverlayRGBA     ));
    y = _mm_load_si128((__m128i const*)(aOverlayRGBA + 16));
    z = _mm_load_si128((__m128i const*)(aOverlayRGBA + 32));
    w = _mm_load_si128((__m128i const*)(aOverlayRGBA + 48));

    // compute blend mask, put 0xFF in bytes equal to zero
    p = _mm_cmpeq_epi8(x, ocmp);
    q = _mm_cmpeq_epi8(y, ocmp);
    r = _mm_cmpeq_epi8(z, ocmp);
    s = _mm_cmpeq_epi8(w, ocmp);

    // align overlay to be condensed to 3-byte color
    x = _mm_shuffle_epi8(x, ovalshf1);
    y = _mm_shuffle_epi8(y, ovalshf2);
    z = _mm_shuffle_epi8(z, ovalshf3);
    w = _mm_shuffle_epi8(w, ovalshf4);

    // condense overlay to 3-btye color
    x = _mm_blendv_epi8(x, y, blndmsk1);
    y = _mm_blendv_epi8(y, z, blndmsk2);
    z = _mm_blendv_epi8(z, w, blndmsk3);

    // align blend mask to be condensed to 3-byte color
    p = _mm_shuffle_epi8(p, omskshf1);
    q = _mm_shuffle_epi8(q, omskshf2);
    r = _mm_shuffle_epi8(r, omskshf3);
    s = _mm_shuffle_epi8(s, omskshf4);

    // condense blend mask to 3-btye color
    p = _mm_blendv_epi8(p, q, blndmsk1);
    q = _mm_blendv_epi8(q, r, blndmsk2);
    r = _mm_blendv_epi8(r, s, blndmsk3);

    // select from overlay and source based on blend mask
    x = _mm_blendv_epi8(x, a, p);
    y = _mm_blendv_epi8(y, b, q);
    z = _mm_blendv_epi8(z, c, r);

    // write colors to destination
    _mm_store_si128((__m128i*)(aDestinationRGB     ), x);
    _mm_store_si128((__m128i*)(aDestinationRGB + 16), y);
    _mm_store_si128((__m128i*)(aDestinationRGB + 32), z);

    // update poniters
    aSourceRGB      += 48;
    aOverlayRGBA    += 64;
    aDestinationRGB += 48;
  }
}

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top