Tengo problemas para distinguir la diferencia práctica entre llamar glFlush()
y glFinish()
.
Los documentos dicen que glFlush()
y glFinish()
empujarán todas las operaciones almacenadas en búfer a OpenGL para que uno pueda estar seguro de que todas se ejecutarán, la diferencia es que glFlush()
regresa inmediatamente donde como glFinish()
bloques hasta que se completan todas las operaciones.
Después de leer las definiciones, pensé que si usaba glFlush()
eso probablemente me encontraría con el problema de enviar más operaciones a OpenGL de las que podría ejecutar. Entonces, solo para probar, cambié mi glFinish()
por a glFlush()
y he aquí, mi programa se ejecutó (por lo que pude ver), exactamente lo mismo; velocidades de cuadro, uso de recursos, todo era igual.
Entonces me pregunto si hay mucha diferencia entre las dos llamadas, o si mi código hace que se ejecuten de la misma manera. O dónde debería usarse uno frente al otro. También pensé que OpenGL tendría alguna llamada glIsDone()
para verificar si todos los comandos almacenados en búfer para a 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();
}
Como han insinuado las otras respuestas, realmente no hay una buena respuesta según las especificaciones. La intención general
glFlush()
es que después de llamarlo, la CPU del host no tenga que hacer ningún trabajo relacionado con OpenGL: los comandos se habrán enviado al hardware de gráficos. La intención general deglFinish()
es que después de que regrese, no quede trabajo restante, y los resultados deberían estar disponibles también en todas las API 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 un montón de libertad en cuanto a lo que es legal.fuente
Siempre estuve confundido acerca de esos dos comandos también, pero esta imagen me lo dejó todo 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 aún no se emiten porque la cola aún no está llena.
En el medio vemos cómo
glFlush()
afectan 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 indica al controlador que es posible que no estemos enviando comandos adicionales. Por lo tanto, esperar a que se llene la cola sería una pérdida de tiempo.En la parte inferior vemos un ejemplo usando
glFinish()
. Casi hace lo mismo queglFlush()
, excepto que hace que el hilo de llamada espere hasta que el hardware haya procesado todos los comandos.Imagen extraída del libro "Programación avanzada de gráficos con OpenGL".
fuente
glFlush
yglFinish
hago, y no puedo decir lo que dice esa imagen. ¿Qué hay del lado izquierdo y del derecho? Además, ¿esa imagen fue publicada en el dominio público o bajo alguna licencia que le permite publicarla en Internet?Si no vio ninguna diferencia de rendimiento, significa que está haciendo algo mal. Como algunos otros mencionaron, tampoco necesita llamar, pero si llama a glFinish, automáticamente está perdiendo el paralelismo que la GPU y la CPU pueden lograr. Déjame sumergirme más profundo:
En la práctica, todo el trabajo que envía al controlador se agrupa y se envía al hardware potencialmente mucho más tarde (por ejemplo, en el momento de SwapBuffer).
Por lo tanto, si está llamando a glFinish, esencialmente está forzando al controlador a enviar los comandos a la GPU (que había almacenado por lotes hasta entonces, y nunca le pidió a la GPU que trabajara) y paralizar la CPU hasta que los comandos presionados estén completamente ejecutado. Entonces, durante todo el tiempo que la GPU funciona, la CPU no (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 perjudicar tu rendimiento. (Esta es una aproximación, ya que los controladores pueden comenzar a hacer que la GPU funcione en algunos comandos si muchos de ellos ya estaban en lotes. 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 glFinish entonces? Las únicas veces que lo usé fueron cuando tuve errores de controlador. De hecho, si uno de los comandos que envía al hardware bloquea la GPU, la opción más sencilla para identificar qué comando es el culpable es llamar a glFinish después de cada Draw. De esa manera, puede reducir qué desencadena exactamente el bloqueo
Como nota al margen, las API como Direct3D no son compatibles con el concepto Finish en absoluto.
fuente
glFlush realmente se remonta a un modelo de servidor cliente. 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 almacenar en búfer. glFlush solo dice "envíe 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 OpenGL local se almacene en búfer y solo emite comandos directamente. Además, todos los comandos que provocan un renderizado real realizarán una descarga implícita.
glFinish, por otro lado, se hizo para medir el rendimiento. Una especie de PING al servidor GL. Envía un comando de ida y vuelta y espera hasta que el servidor responde "Estoy inactivo".
Sin embargo, hoy en día los conductores locales modernos tienen ideas bastante creativas sobre lo que significa estar inactivo. ¿Es "todos los píxeles están dibujados" o "mi cola de comandos tiene espacio"? También debido a que muchos programas antiguos esparcían glFlush y glFinish a lo largo de su código sin ninguna 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 sin operaciones en la práctica, a menos que esté codificando para un antiguo servidor SGI OpenGL remoto.
fuente
Echa un vistazo aquí . En resumen, dice:
Otro artículo describe otras diferencias:
glFlush
glFinish
obliga a OpenGL a realizar comandos sobresalientes, lo cual es una mala idea (por ejemplo, con VSync)En resumen, esto significa que ni siquiera necesita estas funciones cuando usa doble búfer, excepto si su implementación de swap-búfer no vacía automáticamente los comandos.
fuente
No parece haber una forma de consultar el estado del búfer. Existe esta extensión de Apple que podría tener el mismo propósito, pero no parece multiplataforma (no la he probado). A simple vista, parece que antes de
flush
presionar el comando de valla; 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
los comandos antes de almacenar en búfer, pero antes de comenzar a renderizar el siguiente cuadro al que llamafinish
. Esto le permitiría comenzar a procesar el siguiente cuadro a medida que funciona la GPU, pero si no lo hace para cuando regrese,finish
se bloqueará para asegurarse de que todo esté en un estado nuevo.No he probado 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 se usó
finish
).Cuando lo cambié a
flush
al final yfinish
al comienzo, 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 método mejor.A modo de comparación, lo quité
finished
del inicio del cuadro , me fuiflush
, y funcionó igual.Entonces, diría use
flush
yfinish
, porque cuando el búfer está vacío en la llamada afinish
, no hay impacto en el rendimiento. Y supongo que si el búfer estuviera lleno, debería querer hacerlo definish
todos modos.fuente
La pregunta es: ¿desea que su código continúe ejecutándose mientras se ejecutan los comandos de OpenGL, o solo que se ejecute después de que se hayan ejecutado sus comandos de OpenGL?
Esto puede ser importante en casos, como retrasos en la red, para tener cierta salida de consola solo después de que se hayan dibujado las imágenes o algo así.
fuente