c++ - iluminacion - opengl linux
opengl: glFlush() vs. glFinish() (8)
Tengo problemas para distinguir la diferencia práctica entre llamar a glFlush()
y glFinish()
.
Los documentos dicen que glFlush()
y glFinish()
enviarán todas las operaciones almacenadas a OpenGL para que se pueda asegurar que se ejecutarán todas, la diferencia es que glFlush()
regresa inmediatamente donde glFinish()
bloquea hasta que todas las operaciones sean completar.
Después de leer las definiciones, pensé que si usaba glFlush()
, probablemente me encontraría con el problema de enviar más operaciones a OpenGL de las que podría ejecutar. Así que, solo para probar, glFinish()
mi glFinish()
por un glFlush()
y he aquí, mi programa se ejecutó (por lo que pude ver), exactamente lo mismo; velocidades de fotogramas, uso de recursos, todo era lo mismo.
Así que me pregunto si hay mucha diferencia entre las dos llamadas, o si mi código las hace funcionar de manera diferente. O donde uno debe ser usado vs. el otro. También pensé que OpenGL tendría alguna llamada como glIsDone()
para comprobar si todos los comandos almacenados en un glFlush()
están completos o no (por lo que uno no envía operaciones a OpenGL más rápido de lo que se pueden ejecutar), pero No pude encontrar tal función.
Mi código es el típico bucle del juego:
while (running) {
process_stuff();
render_stuff();
}
Como las otras respuestas han insinuado, realmente no hay una buena respuesta según la especificación. El objetivo general de glFlush()
es que después de llamarlo, la CPU del host no tendrá que realizar ningún trabajo relacionado con OpenGL: los comandos se habrán enviado al hardware de gráficos. La intención general de glFinish()
es que después de que regrese, no quede trabajo restante, y los resultados deberían estar disponibles también todas las API apropiadas que no sean OpenGL (por ejemplo, lecturas del framebuffer, capturas de pantalla, etc.). Si eso es realmente lo que sucede depende del conductor. La especificación permite una tonelada de latitud en cuanto a lo que es legal.
Echa un vistazo here . En resumen, dice:
glFinish () tiene el mismo efecto que glFlush (), con la adición de que glFinish () se bloqueará hasta que se hayan ejecutado todos los comandos enviados.
Otro article describe otras diferencias:
- Las funciones de intercambio (usadas en aplicaciones con búfer doble)
glFlush
automáticamente los comandos, por lo que no es necesario llamar aglFlush
-
glFinish
obliga a OpenGL a ejecutar comandos excepcionales, lo cual es una mala idea (por ejemplo, con VSync)
En resumen, esto significa que ni siquiera necesita estas funciones cuando utiliza el doble almacenamiento en búfer, excepto si su implementación de intercambio de búferes no vacía automáticamente los comandos.
La pregunta es: ¿desea que su código continúe ejecutándose mientras se ejecutan los comandos OpenGL, o solo para ejecutar después de que se hayan ejecutado sus comandos OpenGL?
Esto puede ser importante en casos, como por sobre retrasos en la red, para tener cierta salida de la consola solo después de que las imágenes se hayan dibujado o tal.
No parece haber una forma de consultar el estado del buffer. Existe esta extensión de Apple que podría servir para el mismo propósito, pero no parece ser multiplataforma (no lo he probado). A primera vista, parece que antes de flush
empujarías el comando de la cerca; a continuación, puede consultar el estado de esa cerca a medida que se mueve a través del búfer.
Me pregunto si podría usar flush
antes de los comandos de almacenamiento en búfer, pero antes de comenzar a renderizar el próximo fotograma al que llama finish
. Esto le permitirá comenzar a procesar el siguiente fotograma mientras la GPU funciona, pero si no está listo para el momento en que regrese, finish
para asegurarse de que todo esté fresco.
No he intentado esto, pero lo haré en breve.
Lo he probado en una aplicación antigua que tiene un uso bastante uniforme de CPU y GPU. (Originalmente usó finish
).
Cuando lo cambié para flush
al final y finish
al principio, no hubo problemas inmediatos. (¡Todo parecía estar bien!) La capacidad de respuesta del programa aumentó, probablemente porque la CPU no se detuvo esperando en la GPU. Definitivamente un mejor método.
Para la comparación, eliminé el finished
desde el inicio del cuadro, dejando el flush
y lo hice de la misma manera.
Así que yo diría usar " flush
and finish
, porque cuando el buffer está vacío en la llamada para finish
, no hay ningún hit de rendimiento. Y supongo que si el búfer estuviera lleno, deberías querer finish
todos modos.
Si no vio ninguna diferencia en el rendimiento, significa que está haciendo algo mal. Como mencionaron algunos otros, tampoco es necesario que llames, pero si llamas glFinish, estás perdiendo automáticamente el paralelismo que la GPU y la CPU pueden alcanzar. Déjame bucear más profundo:
En la práctica, todo el trabajo que envía al controlador se procesa por lotes y se envía al hardware potencialmente mucho más tarde (por ejemplo, en el horario de SwapBuffer).
Entonces, si estás llamando glFinish, esencialmente estás obligando al controlador a enviar los comandos a la GPU (que se ha procesado hasta entonces, y nunca le pediste a la GPU que trabaje), y atascar la CPU hasta que los comandos empujados estén completamente ejecutado. Entonces, durante todo el tiempo que la GPU funciona, la CPU no funciona (al menos en este hilo). Y todo el tiempo que la CPU hace su trabajo (principalmente comandos de procesamiento por lotes), la GPU no hace nada. Así que sí, glFinish debería dañar tu rendimiento. (Esta es una aproximación, ya que los controladores pueden comenzar a hacer que la GPU trabaje en algunos comandos si muchos de ellos ya estaban agrupados. No es típico, ya que los almacenamientos intermedios de comando tienden a ser lo suficientemente grandes como para contener muchos comandos).
Ahora, ¿por qué llamarías glFinish entonces? Las únicas veces que lo he usado fueron cuando tuve errores en el controlador. De hecho, si uno de los comandos que envía al hardware bloquea la GPU, entonces su opción más simple para identificar qué comando es el culpable es llamar a glFinish después de cada Sorteo. De esta forma, puede reducir lo que desencadena exactamente el bloqueo
Como nota al margen, las API como Direct3D no son compatibles con un concepto de acabado en absoluto.
Siempre estaba confundido acerca de esos dos comandos también, pero esta imagen me lo dejó bien claro: Aparentemente, algunos controladores de GPU no envían los comandos emitidos al hardware a menos que se haya acumulado una cierta cantidad de comandos. En este ejemplo, ese número es 5 .
La imagen muestra varios comandos OpenGL (A, B, C, D, E ...) que se han emitido. Como podemos ver en la parte superior, los comandos todavía no se emiten porque la cola todavía no está completa.
En el medio vemos cómo glFlush()
afecta los comandos en cola. Le dice al controlador que envíe todos los comandos en cola al hardware (incluso si la cola aún no está llena). Esto no bloquea el hilo de llamada. Simplemente le indica al conductor que tal vez no estemos enviando ningún comando adicional. Por lo tanto, esperar a que la cola se llene sería una pérdida de tiempo.
En la parte inferior vemos un ejemplo usando glFinish()
. Hace casi lo mismo que glFlush()
, excepto que hace que el hilo de llamada espere hasta que el hardware haya procesado todos los comandos.
Imagen tomada del libro "Programación avanzada de gráficos usando OpenGL".
Tenga en cuenta que estos comandos existen desde los primeros días de OpenGL. glFlush garantiza que los comandos OpenGL anteriores deben completarse en tiempo definido ( especificaciones de OpenGL 2.1 , página 245). Si dibuja directamente en el buffer frontal, esto asegurará que los controladores OpenGL comiencen a dibujar sin demasiada demora. Podría pensar en una escena compleja que aparece objeto tras objeto en la pantalla, cuando llama a glFlush después de cada objeto. Sin embargo, al usar doble buffering, glFlush prácticamente no tiene ningún efecto, ya que los cambios no serán visibles hasta que intercambies los buffers.
glFinish no regresa hasta que todos los efectos de los comandos emitidos previamente [...] se hayan realizado completamente . Esto significa que la ejecución de su programa espera aquí hasta que se dibuje cada último píxel y OpenGL no tiene nada más que hacer. Si renderiza directamente en el buffer frontal, glFinish es la llamada a hacer antes de usar las llamadas del sistema operativo para tomar capturas de pantalla. Es mucho menos útil para el doble almacenamiento en memoria intermedia, porque no ve los cambios que obligó a completar.
Entonces, si usa el doble buffering, probablemente no necesitará ni glFlush ni glFinish. SwapBuffers dirige implícitamente las llamadas de OpenGL al búfer correcto, no es necesario llamar primero a glFlush . Y no le moleste estresar el controlador OpenGL: glFlush no se ahogará en demasiados comandos. No se garantiza que esta llamada regrese de inmediato (sea lo que sea que eso signifique), por lo que puede tomar cualquier tiempo que necesite procesar sus comandos.
glFlush realmente se remonta a un modelo de servidor de cliente. Envía todos los comandos gl a través de una tubería a un servidor gl. Esa tubería puede amortiguar. Al igual que cualquier archivo o red de E / S podría amortiguar. glFlush solo dice "envía el búfer ahora, ¡incluso si aún no está lleno!". En un sistema local, esto casi nunca es necesario porque es poco probable que una API local de OpenGL se guarde en un búfer y simplemente emite comandos directamente. Además, todos los comandos que causan la representación real harán una descarga implícita.
glFinish por otro lado se hizo para la medición del rendimiento. Tipo de PING para el servidor GL. Reduce un comando y espera hasta que el servidor responda "Estoy inactivo".
Hoy en día, los conductores locales modernos tienen ideas bastante creativas de lo que significa estar ociosos. ¿Se "dibujaron todos los píxeles" o "mi cola de comandos tiene espacio"? Además, debido a que muchos programas antiguos rociaron glFlush y glFinish en todo su código sin motivo alguno, como la codificación vudú, muchos controladores modernos simplemente los ignoran como una "optimización". No puedo culparlos por eso, realmente.
Así que en resumen: trate tanto glFinish como glFlush como ninguna operación en la práctica a menos que esté codificando para un antiguo servidor remoto SGI OpenGL.