Pasar una lista de valores a fragment shader


Quiero enviar una lista de valores a un sombreador de fragmentos. Es una lista posiblemente grande (un par de miles de artículos largos) de flotadores de precisión individuales. El sombreador de fragmentos necesita acceso aleatorio a esta lista y quiero actualizar los valores de la CPU en cada fotograma.

Estoy considerando mis opciones sobre cómo se podría hacer esto:

  1. Como variable uniforme de tipo array ("flotador uniforme x[10];"). Pero parece que hay límites aquí, en mi GPU el envío de más de unos pocos cientos values es muy lento y también tendría que codificar el límite superior en el sombreador cuando preferiría cambiar eso en tiempo de ejecución.

  2. Como una textura con altura 1 y ancho de mi lista, luego actualice los datos usando glCopyTexSubImage2D.

  3. Otros métodos? No me he mantenido al día con todos los cambios en la especificación GL últimamente, tal vez hay algún otro método que está específicamente diseñado para este propósito?

 60
Author: Peter O., 2011-10-31

4 answers

Actualmente hay 4 formas de hacer esto: texturas 1D estándar, texturas de búfer, búferes uniformes y búferes de almacenamiento de sombreadores.

Texturas 1D

Con este método, utiliza glTex(Sub)Image1D para rellenar una textura 1D con sus datos. Dado que sus datos son solo una matriz de flotadores, su formato de imagen debe ser GL_R32F. A continuación, se accede a él en el sombreador con una simple llamada texelFetch. texelFetch toma las coordenadas de texel (de ahí el nombre), y apaga todo el filtrado. Así que tienes exactamente uno texel.

Nota: texelFetch es 3.0+. Si desea utilizar versiones anteriores de GL, deberá pasar el tamaño al sombreador y normalizar la coordenada de textura manualmente.

Las principales ventajas aquí son la compatibilidad y la compacidad. Esto funcionará en hardware GL 2.1 (usando la notación). Y no tienes para usar formatos GL_R32F; podrías usar GL_R16F half-floats. O GL_R8 si sus datos son razonables para un byte normalizado. El tamaño puede significar mucho para el total rendimiento.

La principal desventaja es la limitación de tamaño. Está limitado a tener una textura 1D del tamaño máximo de textura. En la LG 3.hardware de clase x, esto será alrededor de 8,192, pero se garantiza que no será menos de 4,096.

Objetos de Búfer uniformes

La forma en que esto funciona es que declaras un bloque uniforme en tu shader:{[19]]}

layout(std140) uniform MyBlock
{
  float myDataArray[size];
};

Luego accedes a esos datos en el sombreador como si fuera una matriz.

De vuelta en el código C / C++ / etc, se crea un objeto buffer y llenarlo con datos de punto flotante. Luego, puede asociar ese objeto buffer con el bloque uniforme MyBlock. Puede encontrar más detalles aquí.

Las principales ventajas de esta técnica son la velocidad y la semántica. La velocidad se debe a cómo las implementaciones tratan los búferes uniformes en comparación con las texturas. Las recuperaciones de texturas son accesos globales a la memoria. Los accesos uniformes al búfer generalmente no lo son; los datos uniformes del búfer generalmente se cargan en el sombreador cuando el sombreador se inicializa su uso en renderizado. Desde allí, es un acceso local, que es mucho más rápido.

Semánticamente, esto es mejor porque no es solo una matriz plana. Para sus necesidades específicas, si todo lo que necesita es un float[], eso no importa. Pero si usted tiene una estructura de datos más compleja, la semántica puede ser importante. Por ejemplo, considere una serie de luces. Las luces tienen una posición y un color. Si utiliza una textura, su código para obtener la posición y el color de una luz en particular se ve como esto:

vec4 position = texelFetch(myDataArray, 2*index);
vec4 color = texelFetch(myDataArray, 2*index + 1);

Con buffers uniformes, se ve igual que cualquier otro acceso uniforme. Has nombrado miembros que se pueden llamar position y color. Así que toda la información semántica está ahí; es más fácil entender lo que está pasando.

También hay limitaciones de tamaño para esto. OpenGL requiere que las implementaciones proporcionen al menos 16.384 bytes para el tamaño máximo de bloques uniformes. Lo que significa, para matrices flotantes, se obtiene sólo 4.096 elementos. Tenga en cuenta de nuevo que este es el mínimo requerido de las implementaciones; algunos hardware pueden ofrecer búferes mucho más grandes. AMD proporciona 65.536 en su hardware de clase DX10, por ejemplo.

Texturas de búfer

Estas son una especie de "textura super 1D". Efectivamente le permiten acceder a un objeto buffer desde una unidad de textura. Aunque son unidimensionales, no son texturas 1D.

Solo se pueden usar desde GL 3.0 o superior. Y solo se puede acceder a ellos a través de la texelFetch función.

La principal ventaja aquí es el tamaño. Las texturas de búfer generalmente pueden ser bastante gigantescas. Mientras que la especificación es generalmente conservadora, mandando al menos 65,536 bytes para texturas de búfer, la mayoría de las implementaciones de GL les permiten variar en el tamaño de megabytes. De hecho, por lo general, el tamaño máximo está limitado por la memoria GPU disponible, no por los límites de hardware.

Además, las texturas de búfer se almacenan en objetos de búfer, no en los objetos de textura más opacos como las texturas 1D. Esto significa que puede usar algunas técnicas de streaming de objetos de búfer para actualizarlos.

La principal desventaja aquí es el rendimiento, al igual que con las texturas 1D. Las texturas de búfer probablemente no serán más lentas que las texturas 1D, pero tampoco serán tan rápidas como las UBOs. Si solo estás sacando una carroza de ellos, no debería ser una preocupación. Pero si estás extrayendo muchos datos de ellos, considera usar una UBO en su lugar.

Objetos de Búfer de Almacenamiento de Sombreadores

OpenGL 4.3 proporciona otra forma de manejar esto: shader storage buffers. Se parecen mucho a los buffers uniformes; los especificas usando una sintaxis casi idéntica a la de los bloques uniformes. La diferencia principal es que puedes escribirles. Obviamente eso no es útil para sus necesidades, pero hay otras diferencias.

Los búferes de almacenamiento de sombreadores son, conceptualmente hablando, una forma alternativa de textura de búfer. Por lo tanto, los límites de tamaño para los búferes de almacenamiento de sombreadores son un lote más grande que para los topes uniformes. El mínimo de OpenGL para el tamaño máximo de UBO es 16KB. El mínimo de OpenGL para el tamaño máximo de SSBO es 16MB. Así que si tienes el hardware, son una alternativa interesante a UBOs.

Solo asegúrate de declararlos como readonly, ya que no les estás escribiendo.

La desventaja potencial aquí es el rendimiento de nuevo, en relación con UBOs. Los SSBOS funcionan como una operación de carga/almacenamiento de imágenes a través de texturas de búfer. Básicamente, es (muy agradable) azúcar sintáctico alrededor de un tipo de imagen imageBuffer. Como tal, las lecturas de estos probablemente se realizarán a la velocidad de las lecturas de un readonly imageBuffer.

Si la lectura a través de carga/almacenamiento de imágenes a través de imágenes de búfer es más rápida o más lenta que las texturas de búfer no está claro en este punto.

Otro problema potencial es que debe cumplir con las reglas para acceso a memoria no síncrona. Estos son complejos y pueden hacer tropezar muy fácilmente.

 118
Author: Nicol Bolas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-18 14:46:00

Esto suena como un buen caso de uso para texture buffer objects. Estos no tienen mucho que ver con texturas regulares y básicamente le permiten acceder a la memoria de un objeto de búfer en un sombreador como una matriz lineal simple. Son similares a las texturas 1D, pero no se filtran y solo se accede por un índice entero, que suena como lo que necesita hacer cuando lo llama una lista de valores. Y también admiten tamaños mucho más grandes que las texturas 1D. Para actualizarlo, puede usar el estándar métodos de objeto buffer (glBufferData, glMapBuffer, ...).

Pero por otro lado requieren hardware GL3/DX10 para usar e incluso se han hecho core en OpenGL 3.1, creo. Si su hardware / controlador no lo admite, entonces su 2da solución sería el método de elección, sino que más bien usaría una textura 1D que una textura width x 1 2D). En este caso, también puede usar una textura 2D no plana y algo de magia de índice para admitir listas más grandes que el tamaño máximo de textura.

Pero los tampones de textura son los perfectos coincide con tu problema, creo. Para obtener información más exacta, también puede mirar la especificación de extensión correspondiente.

EDITAR: En respuesta al comentario de Nicol sobre objetos de búfer uniformes, también puede buscar aquí para una pequeña comparación de los dos. Todavía tiendo a TBOs, pero realmente no puedo razonar por qué, solo porque lo veo un mejor ajuste conceptualmente. Pero tal vez Nicol puede proporcionar un anwer con un poco más de información sobre el asunto.

 6
Author: Christian Rau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-31 17:57:11

Una forma sería usar arreglos uniformes como usted menciona. Otra forma de hacerlo, es usar una "textura"1D. Busque GL_TEXTURE_1D y glTexImage1D. Personalmente prefiero esta forma, ya que no necesita codificar el tamaño de la matriz en el código del sombreador como dijo, y opengl ya tiene funciones integradas para cargar/acceder a datos 1D en la GPU.

 5
Author: edvaldig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-31 14:57:42

Yo diría que probablemente no es el número 1.. tienes un número limitado de registros para los uniformes de sombreado, que varía según la tarjeta. Puede consultar GL_MAX_FRAGMENT_UNIFORM_COMPONENTS para averiguar su límite. En las tarjetas más nuevas que se ejecuta en los miles, por ejemplo, un Quadro FX 5500 tiene 2048, al parecer. (http://www.nvnews.net/vbulletin/showthread.php?t=85925). Depende de qué hardware quieres que se ejecute, y qué otros uniformes que puede ser que desee enviar al sombreador también.

El número 2 podría funcionar dependiendo de sus necesidades. Lo siento por la vaguedad aquí, espero que alguien más pueda darle una respuesta más precisa, pero debe ser explícito en cuántas llamadas de textura realiza en tarjetas de modelos de sombreador más antiguas. También depende de cuántas lecturas de textura quieras hacer por fragmento, probablemente no querrás estar intentando leer 1000 de elementos por fragmento, de nuevo, dependiendo de tu modelo de sombreado y requisitos de rendimiento. Puede empaquetar valores en RGBAs de una textura, lo que le da 4 lecturas por llamada de textura, pero con acceso aleatorio como requisito, esto podría no ayudarlo.

No estoy seguro sobre el número 3, pero sugeriría tal vez mirar UAV (vistas de acceso desordenado) aunque creo que es solo DirectX, sin un equivalente OpenGL decente. Creo que hay una extensión nVidia para OpenGL, pero una vez más se restringe a una especificación mínima bastante estricta.

Es poco probable que pasar 1000 de elementos de datos a su sombreador de fragmentos sea la mejor solución a su problema.. tal vez si usted dio más detalles sobre lo que está tratando de lograr usted puede obtener sugerencias alternativas?

 1
Author: Hybrid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-31 15:06:57