¿Cuándo es un sombreador de cómputo más eficiente que un sombreador de píxeles para el filtrado de imágenes?

37

Las operaciones de filtrado de imágenes como desenfoques, SSAO, floración, etc., generalmente se realizan utilizando sombreadores de píxeles y operaciones de "reunión", donde cada invocación de sombreador de píxeles emite una serie de recuperaciones de textura para acceder a los valores de píxeles vecinos, y calcula el valor de un solo píxel de el resultado. Este enfoque tiene una ineficiencia teórica en que se realizan muchas recuperaciones redundantes: las invocaciones de sombreador cercanas volverán a recuperar muchos de los mismos elementos de texto.

Otra forma de hacerlo es con sombreadores informáticos. Estos tienen la ventaja potencial de poder compartir una pequeña cantidad de memoria en un grupo de invocaciones de sombreadores. Por ejemplo, puede hacer que cada invocación busque un texel y lo almacene en la memoria compartida, luego calcule los resultados desde allí. Esto podría o no ser más rápido.

La pregunta es ¿en qué circunstancias (si alguna vez) es el método de cálculo de sombreado en realidad más rápido que el método de sombreador de píxeles? ¿Depende del tamaño del núcleo, qué tipo de operación de filtrado es, etc.? Claramente, la respuesta variará de un modelo de GPU a otro, pero estoy interesado en saber si hay alguna tendencia general.

Nathan Reed
fuente
Creo que la respuesta es "siempre" si el sombreador de cálculo se realiza correctamente. Esto no es trivial de lograr. Un sombreador de cómputo también coincide mejor que un sombreador de píxeles conceptualmente para algoritmos de procesamiento de imágenes. Sin embargo, un sombreador de píxeles proporciona menos margen de maniobra para escribir filtros de bajo rendimiento.
bernie
@bernie ¿Puede aclarar qué se necesita para que el sombreador de cómputo se "haga correctamente"? Tal vez escribir una respuesta? Siempre es bueno tener más perspectivas sobre el tema. :)
Nathan Reed
2
¡Ahora mira lo que me hiciste hacer! :)
bernie
Además de compartir el trabajo entre subprocesos, la capacidad de usar cómputo asíncrono es una gran razón para usar sombreadores de cómputo.
JarkkoL

Respuestas:

23

Una ventaja arquitectónica de los sombreadores informáticos para el procesamiento de imágenes es que omiten el paso ROP . Es muy probable que las escrituras desde sombreadores de píxeles pasen por todo el hardware de mezcla regular, incluso si no lo usa. En general, los sombreadores de cómputo pasan por una ruta diferente (y a menudo más directa) a la memoria, por lo que puede evitar un cuello de botella que de lo contrario tendría. He oído hablar de ganancias de rendimiento bastante importantes atribuidas a esto.

Una desventaja arquitectónica de los sombreadores de cómputo es que la GPU ya no sabe qué elementos de trabajo se retiran a qué píxeles. Si está utilizando la tubería de sombreado de píxeles, la GPU tiene la oportunidad de empaquetar el trabajo en un warp / wavefront que escribe en un área del destino de renderizado que está contigua en la memoria (que puede estar en mosaico de orden Z o algo así para el rendimiento razones). Si está utilizando una tubería de cómputo, la GPU ya no puede dejar el trabajo en lotes óptimos, lo que lleva a un mayor uso de ancho de banda.

Sin embargo, es posible que pueda convertir ese empaque alterado de urdimbre / frente de onda en una ventaja nuevamente, si sabe que su operación particular tiene una subestructura que puede explotar al empacar el trabajo relacionado en el mismo grupo de subprocesos. Como dijiste, en teoría podrías dar un descanso al hardware de muestreo al muestrear un valor por carril y poner el resultado en la memoria compartida de grupo para que otros carriles puedan acceder sin muestreo. Si esto es una victoria depende de cuán costosa sea la memoria compartida de su grupo: si es más barata que la caché de texturas de nivel más bajo, entonces puede ser una victoria, pero no hay garantía de eso. Las GPU ya se ocupan bastante bien de las capturas de textura altamente locales (por necesidad).

Si tiene etapas intermedias en la operación en las que desea compartir resultados, puede tener más sentido usar memoria compartida en grupo (ya que no puede recurrir al hardware de muestreo de textura sin haber escrito realmente su resultado intermedio en la memoria). Lamentablemente, tampoco puede depender de tener resultados de ningún otro grupo de subprocesos, por lo que la segunda etapa tendría que limitarse solo a lo que está disponible en el mismo mosaico. Creo que el ejemplo canónico aquí es calcular la luminancia promedio de la pantalla para la exposición automática. También podría imaginar combinar el muestreo ascendente de textura con alguna otra operación (dado que el muestreo ascendente, a diferencia del muestreo descendente y los desenfoques, no depende de ningún valor fuera de un mosaico dado).

John Calsbeek
fuente
Dudo seriamente que el ROP agregue alguna sobrecarga de rendimiento si la combinación está deshabilitada.
GroverManheim
@GroverManheim Depende de la arquitectura! El paso de fusión de salida / ROP también tiene que lidiar con las garantías de pedido, incluso si la combinación está deshabilitada. Con un triángulo de pantalla completa no hay ningún peligro de pedido real, pero el hardware puede no saberlo. Puede haber caminos rápidos especiales en el hardware, pero sabiendo con certeza que califica para ellos ...
John Calsbeek
10

John ya ha escrito una gran respuesta, así que considera esta respuesta como una extensión de la suya.

Actualmente estoy trabajando mucho con sombreadores de cómputo para diferentes algoritmos. En general, descubrí que los sombreadores de cómputo pueden ser mucho más rápidos que su sombreador de píxeles equivalente o transformar alternativas basadas en retroalimentación.

Una vez que comprende cómo funcionan los sombreadores informáticos, también tienen mucho más sentido en muchos casos. El uso de sombreadores de píxeles para filtrar una imagen requiere configurar un framebuffer, enviar vértices, usar múltiples etapas de sombreado, etc. ¿Por qué debería ser necesario filtrar una imagen? Estar acostumbrado a representar quads de pantalla completa para el procesamiento de imágenes es sin duda la única razón "válida" para seguir usándolos en mi opinión. Estoy convencido de que un recién llegado al campo de los gráficos de cómputo consideraría que los sombreadores de cómputo son mucho más naturales para el procesamiento de imágenes que el procesamiento de texturas.

Su pregunta se refiere al filtrado de imágenes en particular, por lo que no explicaré demasiado sobre otros temas. En algunas de nuestras pruebas, solo configurar una retroalimentación de transformación o cambiar los objetos de framebuffer para representar una textura podría generar costos de rendimiento de alrededor de 0.2 ms. ¡Tenga en cuenta que esto excluye cualquier representación! En un caso, mantuvimos exactamente el mismo algoritmo portado para calcular sombreadores y vimos un aumento notable en el rendimiento.

Cuando se usan sombreadores informáticos, se puede usar más silicio en la GPU para hacer el trabajo real. Todos estos pasos adicionales son necesarios cuando se utiliza la ruta del sombreador de píxeles:

  • Ensamblaje de vértices (lectura de los atributos de vértices, divisores de vértices, conversión de tipos, expandiéndolos a vec4, etc.)
  • El sombreador de vértices debe programarse sin importar cuán mínimo sea
  • El rasterizador tiene que calcular una lista de píxeles para sombrear e interpolar las salidas de vértice (probablemente solo coordenadas de textura para el procesamiento de imágenes)
  • Todos los diferentes estados (prueba de profundidad, prueba alfa, tijera, mezcla) deben establecerse y administrarse

Se podría argumentar que todas las ventajas de rendimiento mencionadas anteriormente podrían ser negadas por un controlador inteligente. Estarías en lo correcto. Tal controlador podría identificar que está renderizando un quad de pantalla completa sin pruebas de profundidad, etc. y configurar una "ruta rápida" que omite todo el trabajo inútil realizado para admitir sombreadores de píxeles. No me sorprendería si algunos conductores hacen esto para acelerar los pases de postprocesamiento en algunos juegos AAA para sus GPU específicas. Por supuesto, puede olvidarse de dicho tratamiento si no está trabajando en un juego AAA.

Sin embargo, lo que el controlador no puede hacer es encontrar mejores oportunidades de paralelismo ofrecidas por la tubería del sombreador de cómputo. Tome el ejemplo clásico de un filtro gaussiano. Usando sombreadores de cálculo, puede hacer algo como esto (separando el filtro o no):

  1. Para cada grupo de trabajo, divida el muestreo de la imagen de origen entre el tamaño del grupo de trabajo y almacene los resultados en la memoria compartida del grupo.
  2. Calcule la salida del filtro utilizando los resultados de muestra almacenados en la memoria compartida.
  3. Escribir en la textura de salida

El paso 1 es la clave aquí. En la versión del sombreador de píxeles, la imagen de origen se muestrea varias veces por píxel. En la versión del sombreador de cómputo, cada fuente de texto se lee solo una vez dentro de un grupo de trabajo. Las lecturas de textura generalmente usan una caché basada en mosaicos, pero esta caché es aún mucho más lenta que la memoria compartida.

El filtro gaussiano es uno de los ejemplos más simples. Otros algoritmos de filtrado ofrecen otras oportunidades para compartir resultados intermedios dentro de grupos de trabajo utilizando memoria compartida.

Sin embargo, hay una trampa. Los sombreadores informáticos requieren barreras de memoria explícitas para sincronizar su salida. También hay menos salvaguardas para proteger contra accesos errantes a la memoria. Para los programadores con buenos conocimientos de programación paralela, los sombreadores de cómputo ofrecen mucha más flexibilidad. Sin embargo, esta flexibilidad significa que también es más fácil tratar los sombreadores de cómputo como el código C ++ ordinario y escribir código lento o incorrecto.

Referencias

bernie
fuente
El paralelismo de muestreo mejorado que describe es intrigante: tengo un simulador fluido que ya está implementado con sombreadores de cómputo con muchas instancias de múltiples muestras por píxel. pero estoy colgado en un bit: ¿cómo accedo a los píxeles vecinos cuando caerían en un grupo de trabajo diferente? por ejemplo, si tengo un dominio de simulación de 64x64, distribuido en un despacho (2,2,1) de hilos numéricos (16,16,1), ¿cómo obtendría el píxel con id.xy == [15,15] sus píxeles vecinos ?
Tossrock
En ese caso, veo 2 opciones principales. 1) aumente el tamaño del grupo a más de 64 y solo escriba resultados para los 64x64 píxeles. 2) primera muestra 64 + nX64 + n dividida de alguna manera en su grupo de trabajo 64x64 y luego use esa cuadrícula de "entrada" más grande para los cálculos. La mejor solución depende de sus condiciones específicas, por supuesto, y le sugiero que escriba otra pregunta para obtener más información, ya que los comentarios no son adecuados para esto.
Bernie
3

Me topé con este blog: Calcular optimizaciones de sombreadores para AMD

Teniendo en cuenta qué trucos se pueden hacer en el sombreador de cálculo (que son específicos solo para los sombreadores de cálculo), tenía curiosidad por saber si la reducción paralela en el sombreador de cálculo era más rápida que en el sombreador de píxeles. Le envié un correo electrónico al autor, Wolf Engel, para preguntarle si había probado el sombreador de píxeles. Él respondió que sí y cuando escribió la publicación del blog, la versión del sombreador de cómputo era sustancialmente más rápida que la versión del sombreador de píxeles. También agregó que hoy las diferencias son aún mayores. Aparentemente, hay casos en los que usar el sombreador de cómputo puede ser de gran ventaja.

maxest
fuente