opengl: glFlush() vs glFinish()


Estoy teniendo problemas para distinguir la diferencia práctica entre llamar glFlush() y glFinish().

Los documentos dicen que glFlush() y glFinish() empujarán todas las operaciones en búfer a OpenGL para que uno pueda estar seguro de que todas serán ejecutadas, la diferencia es que glFlush() devuelve inmediatamente donde as glFinish() bloquea hasta que todas las operaciones estén completas.

Después de leer las definiciones, pensé que si tuviera que usar glFlush() que probablemente me encontraría con el problema de presentar más operaciones a OpenGL de las que podría ejecutar. Así que, solo para intentarlo, cambié mi glFinish() por un glFlush() y he aquí, mi programa corrió (por lo que pude ver), exactamente el mismo; velocidades de fotogramas, uso de recursos, todo era el mismo.

Así que me pregunto si hay mucha diferencia entre las dos llamadas, o si mi código hace que no se ejecuten de manera diferente. O donde uno debe ser usado en comparación con el otro. También pensé que OpenGL tendría alguna llamada como glIsDone() para verificar si todos los búferes los comandos para 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 de juego:

while (running) {
    process_stuff();
    render_stuff();
}
 90
Author: Vallentin, 2010-01-27

8 answers

Tenga en cuenta que estos comandos existen desde los primeros días de OpenGL. glFlush asegura que los comandos OpenGL anteriores deben completarse en tiempo finito (OpenGL 2.1 specs , página 245). Si dibuja directamente en el búfer frontal, esto garantizará que los controladores OpenGL comiencen a dibujar sin demasiado retraso. 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 tiene prácticamente ningún efecto, ya que los cambios no serán visibles hasta que intercambies los búferes.

GlFinish no regresa hasta que todos los efectos de comandos previamente emitidos [...] se realizan plenamente . Esto significa que la ejecución de su programa espera aquí hasta que se dibuje hasta el último píxel y OpenGL no tiene nada más que hacer. Si renderiza directamente en el búfer frontal, glFinish es la llamada a realizar antes de usar las llamadas al sistema operativo para tomar capturas de pantalla. Es mucho menos útil para doble buffering, porque no ves los cambios que forzaste a completar.

Así que si usas doble buffering, probablemente no necesitarás ni glFlush ni glFinish. SwapBuffers dirige implícitamente las llamadas a OpenGL al búfer correcto, no hay necesidad de llamar primero a glFlush. Y no le importa enfatizar el controlador OpenGL: glFlush no se ahogará con demasiados comandos. No se garantiza que esta llamada devuelva inmediatamente (lo que eso signifique), por lo que puede tomar cualquier tiempo que necesita para procesar sus comandos.

 79
Author: Malte Clasen,
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
2017-06-26 23:09:24

Como las otras respuestas han insinuado, realmente no hay una buena respuesta según la especificación. La intención general de glFlush() es que después de llamarla, la CPU host no tendrá trabajo relacionado con OpenGL que hacer the los comandos se habrán enviado al hardware gráfico. La intención general de glFinish() es que después de que regrese, no queda trabajo restante, y los resultados deben 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.

 21
Author: Andy Ross,
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
2010-01-27 00:00:41

Siempre estuve confundido acerca de esos dos comandos también, pero esta imagen me lo dejó todo claro: Rubor vs Acabado Aparentemente, algunos controladores de GPU no envían los comandos emitidos al hardware a menos que se haya acumulado un cierto número 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 no se emiten todavía, porque la cola no está llena todavía.

En el en medio vemos cómo glFlush() afecta a 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 controlador que puede que no enviemos 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(). Casi hace lo mismo que glFlush(), excepto que hace que el hilo de llamada espere hasta todos los comandos han sido procesados por el hardware.

Imagen tomada del libro "Advanced Graphics Programming Using OpenGL".

 9
Author: Tara,
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
2016-07-21 15:27:30

Si no has visto ninguna diferencia de rendimiento, significa que estás haciendo algo mal. Como algunos otros mencionaron, tampoco es necesario llamar, pero si llama a glFinish, entonces pierde automáticamente el paralelismo que la GPU y la CPU pueden lograr. Déjame bucear más profundo:

En la práctica, todo el trabajo que envías al controlador es agrupado, y enviado al hardware potencialmente mucho más tarde (por ejemplo, a la hora de SwapBuffer).

Entonces, si estás llamando a glFinish, estás esencialmente obligando al controlador a empujar los comandos a la GPU (que se acumuló hasta entonces, y nunca pidió a la GPU para trabajar en), y detener la CPU hasta que los comandos empujados se ejecutan por completo. Así que durante todo el tiempo que la GPU funciona, la CPU no lo hace (al menos en este hilo). Y todo el tiempo que la CPU hace su trabajo (principalmente comandos por lotes), la GPU no hace nada. Así que sí, glFinish debería dañar tu rendimiento. (Esto es una aproximación, ya que los controladores pueden comenzar a tener el trabajo de GPU en algunos comandos si muchos de ellos ya estaban agrupados. Sin embargo, no es típico, ya que los búferes de comandos tienden a ser lo suficientemente grandes como para contener muchos comandos).

Ahora, ¿por qué llamarías a glFinish entonces? Las únicas veces que lo he usado fueron cuando tenía errores de conductor. 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 esa manera, puede reducir qué desencadena exactamente la crash

Como nota al margen, las API como Direct3D no admiten un concepto de Acabado en absoluto.

 6
Author: Bahbar,
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
2010-01-27 20:14:41

GlFlush realmente se remonta a un modelo cliente-servidor. Usted envía todos los comandos gl a través de una tubería a un servidor gl. Esa tubería podría amortiguar. Al igual que cualquier archivo o e/s de red podría buffer. glFlush solo dice " enviar el búfer ahora, incluso si no está lleno todavía!". En un sistema local, esto casi nunca es necesario porque es poco probable que una API OpenGL local se almacene en búfer y solo emita comandos directamente. También todos los comandos que causan el renderizado real harán un vaciado implícito.

GlFinish sobre el otra mano fue hecha para la medición del rendimiento. Una especie de PING al servidor GL. It roundtrips a command and waits until the server responds "I am idle".

Hoy en día, los conductores locales modernos tienen ideas bastante creativas de lo que significa estar inactivo. ¿Es "todos los píxeles se dibujan"o" mi cola de comandos tiene espacio"? También porque muchos programas antiguos rociaron glFlush y glFinish a lo largo de su código sin razón como codificación vudú muchos controladores modernos simplemente los ignoran como una "optimización". No puedo culparlos por eso, de verdad.

En resumen: Trate tanto glFinish como glFlush como no ops en la práctica a menos que esté codificando para un antiguo servidor remoto SGI OpenGL.

 5
Author: starmole,
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
2012-05-22 09:38:28

Echa un vistazo aquí. En resumen, dice:

GlFinish() tiene el mismo efecto que glFlush(), con la adición de que glFinish() bloqueará hasta que todos los comandos enviados hayan sido ejecutados.

Otro artículo describe otras diferencias:

  • Las funciones de intercambio (utilizadas en aplicaciones de doble búfer) limpian automáticamente los comandos, por lo que no es necesario llamar a glFlush
  • glFinish obliga a OpenGL a realizar comandos pendientes, que es un mala idea (por ejemplo, con VSync)

En resumen, esto significa que ni siquiera necesita estas funciones cuando usa doble buffering, excepto si su implementación de swap-buffers no vacía automáticamente los comandos.

 4
Author: AndiDog,
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
2010-01-26 23:03:33

No parece haber una forma de consultar el estado del búfer. Existe esta extensión de Apple que podría servir para el mismo propósito, pero no parece multiplataforma (no lo he probado.) A simple vista, parece que antes de flush empujaría el comando fence; luego puede consultar el estado de esa valla a medida que se mueve a través del búfer.

Me pregunto si podría usar flush antes de cargar comandos en el búfer, pero antes de comenzar a renderizar el siguiente fotograma que llame finish. Esto le permitiría comenzar a procesar el siguiente fotograma a medida que la GPU funciona, pero si no está hecho para cuando regrese, finish se bloqueará para asegurarse de que todo esté en un estado fresco.

No lo he intentado, pero lo haré en breve.

Lo he probado en una aplicación antigua que tiene bastante incluso el uso de CPU y GPU. (Se utilizó originalmente finish.)

Cuando lo cambié a flush al final y finish al principio, no hubo problemas inmediatos. (Todo se veía 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é finished desde el inicio del frame, dejando flush, y realizó lo mismo.

Así que yo diría usar flush y finish, porque cuando el búfer está vacío en la llamada a finish, no hay golpe de rendimiento. Y supongo que si el búfer estaba lleno debería querer finish de todos modos.

 3
Author: GManNickG,
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
2010-01-26 23:08:38

La pregunta es: ¿desea que su código continúe ejecutándose mientras se ejecutan los comandos de OpenGL, o que solo ejecute después de que se hayan ejecutado sus comandos de OpenGL?

Esto puede importar en casos, como retrasos en la red, para tener cierta salida de consola solo después de las imágenes se han dibujado o el tal.

 0
Author: anon,
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
2010-01-26 23:07:04