2011-03-09 17 views
19

Este tema, como con cualquier problema de optimización, recibe mucho, pero no pude encontrar lo que (creo) quiero.OpenGL preguntas de rendimiento de bajo nivel

Una gran cantidad de tutoriales, e incluso las preguntas más importantes tienen consejos similares; generalmente cubre:

  • cara Uso GL sacrificio (la función de OpenGL, no la lógica escena)
  • Enviar sólo 1 matriz a la GPU (combinación projectionModelView), por lo que disminuye los cálculos MVP desde por vértice a una vez por modelo (como debería ser).
  • Use vértices intercalados
  • Minimizar el mayor número posible GL llama, lote, si procede

Y, posiblemente, unos pocos/muchos otros. Estoy (por razones de curiosidad) renderizando 28 millones de triángulos en mi aplicación usando varios buffers de vértices. He probado todas las técnicas anteriores (a mi leal saber y entender) y casi no he tenido cambios en el rendimiento.

Si bien recibo alrededor de 40FPS en mi implementación, lo que de ninguna manera es problemático, ¿todavía tengo curiosidad por saber dónde se utilizan estos "consejos" de optimización?

Mi CPU está al ralentí alrededor del 20-50% durante el procesamiento, por lo tanto, I asumo Tengo una GPU destinada a aumentar el rendimiento.

Nota: Estoy buscando en gDEBugger en el momento

Cruz publicada en Game Development

Respuesta

25

El punto 1 es obvia, como es ahorra tasa de relleno. En caso de que las primitivas de la parte posterior de un objeto se procesen primero, esto omitirá esas caras. Sin embargo, las GPU modernas toleran el sobregiro bastante bien. Una vez (GeForce8800 GTX) medí hasta 20% de sobregiro antes de que se alcanzara un rendimiento significativo. Pero es mejor guardar esta reserva para cosas como la eliminación de oclusiones, la representación de geometría combinada y similares.

El punto 2 es, así, inútil. Las matrices nunca se han calculado en la GPU, bueno, si no se cuenta SGI Onyx. Las matrices siempre fueron solo un tipo de parámetro global de representación calculado en la CPU, luego se incluyeron en los registros globales de la GPU, que ahora se llaman uniformes, por lo que unirlos tiene muy poco beneficio. En el sombreado, solo se ahorra una multiplicación de matriz vectorial adicional (se reduce a 4 instrucciones MAD), a expensas de una menor flexibilidad algorítmica.

El punto 3 es todo acerca de la eficiencia de caché. Los datos que pertenecen juntos deben caber en una línea de caché.

El punto 4 trata de evitar que los cambios de estado dañen las memorias caché. Pero depende en gran medida de a qué GL llaman. Cambiar los uniformes es barato. Cambiar una textura es costoso. La razón es que un uniforme se encuentra en un registro, no en una memoria almacenada en caché. Cambiar un sombreador es costoso, ya que diferentes sombreadores exhiben un comportamiento de tiempo de ejecución diferente, lo que arruina la ejecución de la ejecución de la tubería, altera la memoria (y por lo tanto) los patrones de acceso a la memoria caché, etc.

Pero todas son micro optimizaciones (algunas de ellas con un gran impacto). Sin embargo, recomiendo mirar en grandes optimizaciones de impacto, como implementar un pase Z temprano; utilizando la consulta de oclusión en la primera Z para una discriminación rápida de lotes de geometría completa.Una gran optimización de impacto, que básicamente consiste en resumir muchas micro optimizaciones de punto 4, es ordenar los lotes por costosos estados GL. Así que agrupe todo con shaders comunes, dentro de esos grupos clasifique por textura, etc. Esta agrupación de estado solo afectará los pases de representación visibles. En las primeras Z solo estás probando los resultados en el buffer Z por lo que solo hay transformación de geometría y los sombreadores de fragmentos simplemente pasarán el valor Z.

+0

¡Muy bonito! Gracias. –

+1

Muy buena respuesta. Una pregunta, sin embargo, en su respuesta al punto 2, estoy un poco confundido. Estaba comparando la diferencia entre tener el "modelo * proyección * vista" dentro del sombreador (como variables uniformes, modelo vista enviada cada vez que un modelo cambia); frente a una única variable de matriz uniforme (modelviewprojection) actualizada por modelo, que se calcula (una vez) por la CPU en lugar de por vértice. Seguramente eso ahorraría muchos cálculos? – dcousens

+3

@Daniel: Normalmente no se calcula la matriz MVP en el sombreado. Lo que hace es realizar primero el cálculo modelview_position = MV * vertex_position, y luego clip_position = P * modelview_position. El razonamiento detrás de esto es que, para algunos algoritmos, necesita la posición del vértice transformado de la vista de modelo entremedio, no solo el resultado final de todo el proceso de proyección. Además, las normales de los vértices solo se transforman mediante la transposición inversa de MV, no el MVP completo^T^-1, por lo que esa es otra razón: si desea implementar una iluminación agradable, necesita esas normales transformadas. – datenwolf

1

Esto depende mucho de qué hardware en particular esté ejecutando y cuáles son los escenarios de uso. Las sugerencias de rendimiento de OpenGL tienen sentido para el caso general: la biblioteca es, después de todo, una abstracción de muchas implementaciones de controladores diferentes. Los fabricantes de controladores pueden optimizar de la forma que prefieran bajo el capó, por lo que pueden eliminar cambios de estado redundantes u otras optimizaciones sin su conocimiento. En otro dispositivo, es posible que no. Lo mejor es apegarse a las mejores prácticas para tener una mejor oportunidad de buen rendimiento en una amplia gama de dispositivos.

+0

Bueno, supongo que esto se puede ver mejor, menos optimizaciones específicas para OpenGL, y más cerca de, buenos hábitos (y rendimiento gratificante) para la programación de gráficos. – dcousens

+0

Algunas reglas generales para el uso óptimo de las bibliotecas de gráficos acelerados por hardware actuales serían: No cambie el estado con demasiada frecuencia y Lote por lotes por lotes. Las reglas de optimización, sin embargo, no están escritas en piedra sobre diferentes generaciones de hardware y lo que es cierto hoy en día no era cierto para todo el hardware anterior y puede no ser cierto para hardware futuro. Siempre tenga una apreciación de la memoria caché y las limitaciones y fortalezas del hardware en el que está trabajando. – Luther

+0

La sabiduría que he escuchado es que la optimización de su hardware específico es un juego de tontos, porque el comportamiento puede alterar radicalmente entre generaciones de hardware o incluso entre versiones de controladores. Es mejor optimizar para la API (en este caso, cambio de estado mínimo como se ha dicho) y dejar que el hardware se ponga al día donde ya no puede optimizar. – Jherico

3
  1. no tiene sentido ya que el conductor puede combinar estas matrices para usted (se sabe que son uniformes, por lo que no va a cambiar durante la llamada empate).
  2. sólo si está obligado

CPU La primera cosa que hay que saber es dónde está exactamente su cuello de botella. GPU no es una respuesta, porque es un sistema complejo. El problema real podría estar entre ellos:

  • procesamiento Shader (vértice/fragmento/geometría)
  • tasa de relleno
  • Draw llama al número
  • GPU < -> VMEM (que es donde el intercalado y texturas más pequeñas ayuda)
  • bus de sistema (streaming algunos datos de cada cuadro?)

Es necesario llevar a cabo una serie de pruebas para ver los pro blem. Por ejemplo, dibuje todo en un FBO más grande para ver si se trata de un problema de tasa de llenado (o aumente la cantidad de MSAA). O dibuja todo dos veces para verificar los problemas de sobrecarga de llamadas de sorteo.

+0

¿Puedes explicar un poco más por qué dices que el procesamiento por lotes debe hacerse solo si la aplicación está unida a la CPU? – ashishsony

+0

(la respuesta original fue dada hace 2.5 años, así que estoy tratando de recordar lo que estaba pensando ...). En el lado de la GPU hay una pequeña diferencia entre una sola llamada y 2 partes de la misma. Es la preparación de la llamada en el lado del conductor que recibe un golpe, que se realiza en la CPU. – kvark

3

Solo para agregar mis 2 centavos a las respuestas de @kvark y @datenwolf, me gustaría decir que, si bien los puntos que menciona son consejos básicos de rendimiento de la GPU, una optimización más involucrada depende en gran medida de la aplicación.

En su caso de prueba de geometría pesada, ya está lanzando 28 millones de triángulos * 40 FPS = 1120 millones de triángulos por segundo - esto ya es bastante: la mayoría (no todos, especialmente Fermi) GPU tienen una configuración de triángulo rendimiento de 1 triángulo por ciclo de reloj de GPU. Lo que significa que una GPU que se ejecuta a 800 MHz, por ejemplo, no puede procesar más de 800 millones de triángulos por segundo; esto sin siquiera dibujar un solo píxel. NVidia Fermi puede procesar 4 triángulos por ciclo de reloj.

Si está llegando a este límite (no menciona su plataforma de hardware), no hay mucho que pueda hacer en el nivel OpenGL/GPU. Todo lo que puede hacer es enviar menos geometría, mediante una eliminación más eficiente (trunco ​​u oclusión), o mediante un esquema LOD.

Otra cosa es que los triángulos diminutos lastiman el fillrate ya que los rasterizadores hacen el procesamiento paralelo en bloques cuadrados de píxeles; ver http://www.geeks3d.com/20101201/amd-graphics-blog-tessellation-for-all/.

+0

Enlace interesante, pero podría haber sido clasificado en una declaración de 'bang por dólar con triángulos y píxeles'. Y todavía se relaciona principalmente con los LOD y otras optimizaciones ligeramente diferentes. Buena respuesta; No indiqué mis especificaciones de hardware, ya que no estaba buscando consejos específicos del hardware. – dcousens

Cuestiones relacionadas