performance - ¿Cuándo son las VBO más rápidas que las primitivas de OpenGL "simples"(glBegin())?
graphics vertex-buffer (6)
Después de muchos años de escuchar acerca de Vertex Buffer Objects (VBO), finalmente decidí experimentar con ellos (mis cosas no son normalmente críticas para el rendimiento, obviamente ...)
Describiré mi experimento a continuación, pero para abreviar, veo un rendimiento indistinguible entre el modo directo "simple" (glBegin () / glEnd ()), matriz de vértices (lado de la CPU) y VBO (lado de la GPU) modos de renderizado Estoy tratando de entender por qué ocurre esto, y bajo qué condiciones puedo esperar ver que los VBOs eclipsen significativamente a sus primitivos antepasados.
Detalles del experimento
Para el experimento, generé una nube Gaussiana 3D (estática) de una gran cantidad de puntos. Cada punto tiene información de vértices y colores asociada a él. Luego giré la cámara alrededor de la nube en cuadros sucesivos en una especie de comportamiento de "órbita". De nuevo, los puntos son estáticos, solo el ojo se mueve (a través de gluLookAt ()). Los datos se generan una vez antes de cualquier representación y se almacenan en dos matrices para usar en el ciclo de representación.
Para la representación directa, todo el conjunto de datos se representa en un solo bloque glBegin () / glEnd () con un ciclo que contiene una única llamada cada uno a glColor3fv () y glVertex3fv ().
Para la matriz de vértices y la representación VBO, todo el conjunto de datos se representa con una sola llamada glDrawArrays ().
Luego, simplemente lo ejecuto durante un minuto más o menos en un circuito cerrado y mido el promedio de FPS con el temporizador de alto rendimiento.
Resultados de rendimiento ##
Como se mencionó anteriormente, el rendimiento no se distinguía tanto en mi máquina de escritorio (XP x64, 8 GB de RAM, 512 MB Quadro 1700) y mi computadora portátil (XP32, 4 GB RAM, 256 MB Quadro NVS 110). Sin embargo, se amplió como se esperaba con el número de puntos. Obviamente, también deshabilité vsync.
Resultados específicos de las ejecuciones de portátiles (representación w / GL_POINTS):
glBegin () / glEnd ():
- 1K pts -> 603 FPS
- 10K pts -> 401 FPS
- 100K pts -> 97 FPS
- 1M pts -> 14 FPS
Matrices de vértices (lado CPU):
- 1K pts -> 603 FPS
- 10K pts -> 402 FPS
- 100K pts -> 97 FPS
- 1M pts -> 14 FPS
Objetos de búfer de vértices (lado de la GPU):
- 1K pts -> 604 FPS
- 10K pts -> 399 FPS
- 100K pts -> 95 FPS
- 1M pts -> 14 FPS
Retratifiqué los mismos datos con GL_TRIANGLE_STRIP y obtuve el mismo indistinguible (aunque más lento como era de esperar debido a la rasterización adicional). Puedo publicar esos números también si alguien los quiere. .
Pregunta (s)
- ¿Lo que da?
- ¿Qué debo hacer para obtener la ganancia de rendimiento prometida de las VBO?
- ¿Qué me estoy perdiendo?
14Mpoints / s no es mucho. Es sospechoso. ¿Podemos ver el código completo haciendo el dibujo, así como la inicialización? (compare esos 14M / s con los 240M / s (!) que obtiene Slava Vishnyakov). Es incluso más sospechoso que baje a 640K / s para los sorteos de 1K (comparado con sus 3.8M / s, que parecen coronados por los ~ 3800 SwapBuffers, de todos modos).
Apostaría que la prueba no mide lo que piensas que mide.
Asumiendo que recuerdo esto bien, mi profesor de OpenGL, que es bien conocido en la comunidad de OpenGL, dijo que son más rápidos en geometría estática y que van a dedicar mucho tiempo a un juego típico, esto será tablas y pequeñas entidades estáticas.
Como nota al margen:
El "modo directo" (glBegin / glEnd) no se admite en:
Entonces, si alguna vez planea portar su aplicación a una plataforma móvil (por ejemplo, iPhone), ni siquiera se acostumbre.
Enseño OpenGL en la Universidad y la diapositiva que explica glBegin / glEnd tiene una gran caja roja alrededor con un encabezado "DO NOT USE" en negrita extra.
Usar matrices de vértices es solo dos líneas más y se guardan ciclos desde el principio.
Después de leer el Libro Rojo, recuerdo un pasaje que decía que los VBO son posiblemente más rápidos dependiendo del hardware . Algunos hardware los optimizan, mientras que otros no. Es posible que su hardware no.
Es posible que falten algunas cosas:
Es una suposición descabellada, pero la tarjeta de su computadora portátil podría estar perdiendo este tipo de operación (es decir, emulándola).
¿Está copiando los datos a la memoria de la GPU (a través de
glBufferData
(GL_ARRAY_BUFFER
conGL_STATIC_DRAW
oGL_DYNAMIC_DRAW
param) o está utilizando puntero a matriz principal (no GPU) en la memoria? (Que requiere copiarlo en cada cuadro y, por lo tanto, el rendimiento es lento)¿Está pasando índices como otro búfer enviado a través de
glBufferData
yGL_ELEMENT_ARRAY_BUFFER
?
Si se hacen esas tres cosas, la ganancia de rendimiento es grande. Para Python (v / pyOpenGl) es aproximadamente 1000 veces más rápido en matrices más grandes que un par de 100 elemnts, C ++ hasta 5 veces más rápido, pero en matrices de 50k-10m vértices.
Aquí están los resultados de mi prueba para c ++ (Core2Duo / 8600GTS):
pts vbo glb/e ratio
100 3900 3900 1.00
1k 3800 3200 1.18
10k 3600 2700 1.33
100k 1500 400 3.75
1m 213 49 4.34
10m 24 5 4.80
Así que incluso con 10 metros de vértices era una velocidad de fotogramas normal, mientras que con glB / e era lenta.
Hay muchos factores para optimizar la representación 3D. generalmente hay 4 cuellos de botella:
- CPU (creación de vértices, llamadas a la APU, todo lo demás)
- Bus (CPU <-> transferencia de GPU)
- Vértice (sombreado del vértice sobre la ejecución de la tubería de función fija)
- Pixel (relleno, ejecución del sombreador de fragmentos y rops)
Su prueba está dando resultados sesgados porque tiene una gran cantidad de CPU (y bus) mientras maximiza el rendimiento de vértices o píxeles. Los VBO se utilizan para reducir la CPU (menos llamadas de API, paralelas a las transferencias de DMA de la CPU). Como no estás vinculado a la CPU, no te dan ninguna ganancia. Esta es la optimización 101. En un juego, por ejemplo, la CPU se vuelve preciosa, ya que es necesaria para otras cosas como la IA y la física, no solo para emitir toneladas de llamadas de API. Es fácil ver que escribir datos de vértice (3 flotantes, por ejemplo) directamente en un puntero de memoria es mucho más rápido que llamar a una función que escribe 3 flotantes en la memoria: como mínimo, se guardan los ciclos de la llamada.