Cómo mejorar el rendimiento del procesamiento por lotes

9

Estoy desarrollando un juego 2D basado en sprites para plataformas móviles y estoy usando OpenGL (bueno, en realidad Irrlicht) para renderizar gráficos. Primero implementé el renderizado de sprites de una manera simple: cada objeto del juego se representa como un quad con su propia llamada de sorteo de GPU, lo que significa que si tuviera 200 objetos de juego, haría 200 llamadas de sorteo por cuadro. Por supuesto, esta fue una mala elección y mi juego estaba completamente vinculado a la CPU porque hay una pequeña sobrecarga de CPU en cada llamada de sorteo de GPU. GPU permaneció inactivo la mayor parte del tiempo.

Ahora, pensé que podría mejorar el rendimiento al recopilar objetos en grandes lotes y renderizar estos lotes con solo unas pocas llamadas de sorteo. Implementé el procesamiento por lotes (para que cada objeto del juego que comparte la misma textura se procese en el mismo lote) y pensé que mis problemas se habían ido ... solo para descubrir que mi velocidad de fotogramas era incluso más baja que antes.

¿Por qué? Bueno, tengo 200 (o más) objetos de juego, y se actualizan 60 veces por segundo. Cada cuadro que tengo que volver a calcular la nueva posición (traslación y rotación) para vértices en la CPU (la GPU en plataformas móviles no admite la creación de instancias, por lo que no puedo hacerlo allí), y hacer este cálculo 48000 por segundo (200 * 60 * 4 desde cada sprite tiene 4 vértices) simplemente parece ser demasiado lento.

¿Qué podría hacer para mejorar el rendimiento? Todos los objetos del juego se mueven / giran (casi) cada cuadro, así que realmente tengo que volver a calcular las posiciones de los vértices. La única optimización que se me ocurre es una tabla de búsqueda de rotaciones para no tener que calcularlas. ¿Ayudarían los sprites de punto? ¿Algún truco desagradable? ¿Algo más?

Gracias.

usuario4241
fuente

Respuestas:

5

¿ Usaste mi puerto de irrlicht para Android? Para sprites 2D en Android y iPhone, utilizo los mismos trucos que tú: el procesamiento por lotes. Intento muchas soluciones en OpenGL ES 1.xy 2.x:

  • ordene por z (paralaje) y por textura, realice las transformaciones en la CPU y llame a glDrawArrays o glDrawElements (forma más rápida). Usa una gran textura si puedes.
  • mismo truco con VBO, no más rápido porque para cada cuadro actualizas toda la información. Puede ser útil para sprites estáticos.
  • use OpenGL ES 2.xy use el sombreador Vertex para calcular las posiciones (más lento)
  • use PointSprites (no hay solución si no es un cuadrado y demasiados píxeles transparentes matan la tasa de relleno)
  • usar la extensión gldrawtexoes ...
  • use una llamada para cada sprite (método más lento)

Entonces, como usted, la CPU realiza todas las transformaciones para OGLES 1.xo OGLES 2.x. Si tiene instrucciones de neón, puede usarlas para acelerar sus cálculos.

Ps: en dispositivos iphone o android, no estoy limitado por la CPU pero la tasa de llenado es limitada. Por lo tanto, es muy importante limitar el sobregiro.

Ellis
fuente
Excelente, esto es algo que estaba buscando. No tenía conocimiento de su puerto de Irrlicht, pero ya tengo mi versión de Irrlicht ejecutándose en iOS. Dices que no estás limitado por la CPU: ¿cuántos sprites estás dibujando? ¿Y cuáles son sus framerates, por ejemplo, para 100 sprites en iPhone? Si tengo 200 objetos termino haciendo 48000 cálculos por segundo. Su punto sobre fillrate es bueno.
user4241
Los sprites estáticos (fondo) están en VBO. Yo uso un VBO por paralaje. De lo contrario, tengo de 100 a 200 sprites en Moblox. En todos los iPhones, incluido el 3G, tengo más de 30 fps (como recuerdo). Pero los grandes sprites son muy costosos (problema de velocidad de llenado) ...
Ellis
Estoy trabajando en un motor de partículas, que puedo usar hasta 20 000 partículas con todas las posiciones calculadas en la CPU y tengo 10 fps con configuraciones extremas (en 3GS y iPhone4). Por lo tanto, 1000 sprites deben ser posibles en 3GS o iPhone4 con una buena velocidad de fotogramas.
Ellis
Gracias, muy útil! ¿Cómo estás implementando tu motor de partículas? ¿Supongo que estás jugando con sombreadores?
user4241
Uso sombreadores porque necesito gl_PointSize para configurar cada tamaño de partícula. Ya no trabajo con OGLES 1.x porque los teléfonos viejos no son mi objetivo. Primero, todo mi código era OGLES 1.x, luego OGLES 1.xy OGLES 2.x (sin mejora de rendimiento) y ahora OGLES 2.x (mejora de representación).
Ellis
1

Recomendaría tener un VBO, con cada vértice que contiene la posición / rotación de cada objeto renderizado y el procesamiento por lotes basado en la textura como lo está haciendo. No estoy muy familiarizado con ogl ES, por lo que no estoy seguro de qué versión de glsl admite, pero es posible que incluso pueda agrupar en función de un conjunto de texturas y almacenar cuál de las 4 texturas que está pasando estarías usando dentro del vértice. Los sprites de punto definitivamente mejorarían su rendimiento porque reducirían drásticamente la cantidad de datos que está enviando, y el procesamiento por lotes nunca debería disminuir el rendimiento si lo hace correctamente. Además, podría mejorar un poco el rendimiento calculando la rotación en el sombreador y solo pasando un valor int / float en los parámetros o dentro del vértice mismo. (los parámetros serían más rápidos,

sringer
fuente
Gracias por su respuesta. Su sugerencia sobre cómo hacer el cálculo de rotación en el sombreador es excelente, pero desafortunadamente estoy usando OpenGL ES 1, que no admite sombreadores, por lo que estoy atascado con una tubería fija. Intentaré sprites puntuales, pero no puedo usarlos en todos los casos porque hay un límite superior para su tamaño. Todavía soy un poco pesimista sobre VBO, si vuelvo a calcular la posición de cada vértice en cada cuadro, ¿cómo ayuda VBO?
user4241
permite que sus datos de vértice permanezcan en la gpu, lo que disminuye la cantidad de datos que tiene que enviar a la gpu en cada fotograma. no necesita sombreadores para aprovechar esto, no debería necesitar cambiar los datos del vértice en absoluto, si tiene una posición base (como el origen) para cada sprite, simplemente puede alterar la matriz mundial se transforma antes de llamar a draw. sin embargo, esto puede ser difícil cuando se procesa en lotes. usando una función fija, probablemente sería más beneficioso simplemente cambiar a VBO y dejar el lote por ahora al menos, eso definitivamente le dará un impulso.
sringer
Entiendo tu argumento. Entonces, después de todo, no estás hablando de lotes sino simplemente usando una llamada de sorteo para dibujar un objeto del juego. Definitivamente probaré cómo VBO sin procesamiento por lotes afecta a FPS en mi juego, pero aún así 200 llamadas de sorteo por cuadro suenan demasiado grandes ... pero supongo que tengo que vivir con eso entonces. Aceptaré su respuesta si no aparecen otras respuestas.
user4241
1

Menciona plataformas móviles que no tienen instancias. Pero, todavía tienes sombreadores de vértices, ¿no?

En ese caso, aún puede hacer pseudo instancing, que también es muy rápido. Haga un VBO (GL_STATIC_DRAW) con los puntos de esquina (en relación con el punto central del sprite, por ejemplo, -1 / -1, 1 / -1, 1/1, -1/1) y las coordenadas de textura que necesite, en él .
Luego establezca uno de los atributos de vértice genéricos para cada llamada de dibujo al punto central del sprite, y dibuje los dos triángulos con el búfer enlazado. Dentro del sombreador de vértices, lea el atributo de vértice genérico y agregue las coordenadas del vértice.

Eso te ahorrará el bloqueo en una transferencia de datos para cada sprite y debería ser mucho más rápido. El número real de llamadas de extracción no es tan terriblemente importante, lo es el bloqueo / bloqueo en el medio.

dm.skt
fuente
Esto suena una buena solución para OpenGL ES 2.0. Desafortunadamente estoy usando ES 1 que no tiene sombreadores en absoluto.
user4241
0

El problema reside en la cantidad de datos que está enviando a la GPU cada fotograma. Simplemente cree un VBO para cada lote y complételo una vez, luego aplique las matrices de transformación correspondientes (a través de glMultMatrix o un sombreador si está utilizando ES 2.0) al dibujar los lotes.

r2d2rigo
fuente
No entiendo cómo ayuda esto cuando tengo 200 objetos de juego separados con transformaciones únicas. Usar glMultMatrix aplicaría la misma transformación a todos los objetos, que no es lo que quiero. Además, enviar datos a la GPU no es un cuello de botella; Si elimino las transformaciones del lado de la CPU, el rendimiento es muy bueno.
user4241
Sí, pero un VBO aún podría mejorar el rendimiento si se aplica correctamente. ¿Cómo representa actualmente sus 200 objetos? ¿Estás usando glBegin / glEnd?
TheBuzzSaw
1
Estoy usando el motor Irrlicht 3D con un nodo de escena personalizado, así que no estoy usando OpenGL directamente (pero supongo que está usando glBegin / glEnd simple en este caso). ¿VBO realmente ayudaría ya que tendría que modificar todo el búfer en cada cuadro? Además, esto no resuelve el problema fundamental de estar vinculado a la CPU debido a los cálculos de transformación de vértice. ¡Pero gracias por tus respuestas de todos modos!
user4241