2010-01-26 38 views
85

Tengo problemas para distinguir la diferencia práctica entre llamar a glFlush() y glFinish().opengl: glFlush() vs. glFinish()

Los documentos dicen que glFlush() y glFinish() impulsará todas las operaciones de búfer a OpenGL modo que uno puede estar seguro de que todos serán ejecutados, con la diferencia de que glFlush() vuelve inmediatamente donde como glFinish() bloques hasta que todas las operaciones se han completado.

Después de leer las definiciones, calculé que si tuviera que usar 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 intentarlo, cambié mi glFinish() por un glFlush() y he aquí, mi programa se ejecutó (por lo que pude ver), exactamente el 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 hace que funcionen no diferente. O donde uno debe ser usado vs. el otro. También calculé que OpenGL tendría alguna llamada como glIsDone() para verificar si todos los comandos almacenados en un glFlush() están completos o no (para que uno no envíe operaciones a OpenGL más rápido de lo que se pueden ejecutar), pero pude encontrar no tal función.

Mi código es el típico bucle del juego:

while (running) { 
    process_stuff(); 
    render_stuff(); 
} 

Respuesta

76

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 (OpenGL 2.1 specs, 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 [...] previamente emitidos se realicen por completo. 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.

Por lo tanto, si utiliza doble almacenamiento en búfer, probablemente no necesitará ni glFlush ni glFinish. SwapBuffers dirige implícitamente las llamadas de OpenGL al búfer correcto, there's no need to call glFlush first. Y no le moleste estresar el controlador OpenGL: glFlush no se ahogará en demasiados comandos. No se garantiza que esta llamada devuelva inmediatamente (lo que sea que eso signifique), por lo que puede tomar cualquier tiempo que necesite procesar sus comandos.

+1

¿Qué quiere decir que glFlush "debe completar en tiempo FINITE" y luego decir, que glFlush No se ahogue porque "puede tomar CUALQUIER tiempo que necesite procesar tus comandos"? (Caps mina) ¿No son esos mutuamente exclusivos? – Praxiteles

+1

Mientras termine en algún punto, cumple con los criterios de tiempo FINITE. Algunos controladores no-op esta llamada por completo. – chrisvarnz

+0

SwapBuffers es específico del contexto y solo necesita vaciar el contexto del hilo de llamada. Si se renderizan múltiples contextos, cada uno debe enjuagarse manualmente, ya que no está garantizado que ocurra al intercambiar almacenamientos intermedios a través de otro contexto. – Andreas

4

Tener una mirada 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.

Otra article describe otras diferencias:

  • funciones de intercambio en funcionamiento (utilizados en aplicaciones de doble búfer) a ras de forma automática los comandos, así que no hay necesidad de llamar a glFlush
  • glFinish fuerzas OpenGL para ejecutar comandos en circulación, que es una mala idea (por ejemplo, con VSync)

En resumen, esto significa que ni siquiera necesita estas funciones cuando se utiliza el doble almacenamiento en búfer, excepto si su implementación de intercambio de búferes no vacía automáticamente los comandos.

+1

Él ya lo sabe; está en la pregunta. – GManNickG

+0

Sí, está bien, pero el documento dice que ambos tienen el * mismo * efecto, con pocas diferencias con respecto al sistema de ventanas, por ejemplo. Pero de todos modos, edité mi respuesta;) – AndiDog

+0

Nice call out on the nuance. Se aclara si es necesario llamar a glFlush() y THEN glFinish() - (una pregunta que tuvimos después de leer todo lo anterior). – Praxiteles

3

No parece haber una forma de consultar el estado del búfer. Existe este Apple extension 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, presionaría 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 guardar en el búfer los comandos, pero antes de comenzar a representar el siguiente fotograma llame al finish. Esto le permitiría comenzar a procesar el siguiente fotograma mientras la GPU funciona, pero si no está listo cuando regrese, finish se bloqueará para asegurarse de que todo esté en estado nuevo.

No he probado esto, pero lo haré en breve.

Lo he probado en una aplicación antigua que tiene muy buena CPU & uso de la GPU. (Originalmente se usó finish.)

Cuando lo cambié a 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é finished desde el inicio del cuadro, dejando flush, y se realizó de la misma manera.

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

0

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 consola solo después de las imágenes han sido dibujadas o tal.

21

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 tenga 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 restante, y los resultados deberían estar disponibles también todas las API apropiadas que no sean de 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.

7

Si no vio ninguna diferencia de 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 profundizar:

En la práctica, todo el trabajo que envías al controlador se procesa por lotes y se envía al hardware potencialmente mucho más tarde (por ejemplo, en el horario de SwapBuffer).

Por lo tanto, si está llamando a glFinish, básicamente está forzando al controlador a enviar los comandos a la GPU (que se ha procesado hasta entonces, y nunca le solicitó a la GPU que trabaje), y ha bloqueado la CPU hasta que los comandos que se presionan se ejecutan completamente. 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ía 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 restringir exactamente qué desencadena el bloqueo

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

+5

En "Ahora, ¿por qué llamaría glFinish en todo entonces?". Si está cargando recursos (por ejemplo, texturas) en un hilo y utilizándolos para representarlos en otro, con el intercambio a través de wglShareLists o similares, debe llamar a glFinish antes de indicar al hilo de renderizado que es libre de representar lo que se ha cargado de forma asíncrona. –

+0

Como dije a continuación, esto parece una solución de error de controlador. No puedo encontrar ninguna mención específica de este requisito. En Windows, el controlador debe tener un bloqueo, en Mac debe hacer CGLLockContext. Pero usar glFinish parece muy incorrecto. Para saber cuándo una textura es válida, debe realizar su propio bloqueo o evento en cualquier caso. – starmole

+1

opencl opengl interop docs recomiendan glFinish para la sincronización "Antes de llamar a clEnqueueAcquireGLObjects, la aplicación debe garantizar que todas las operaciones GL pendientes que accedan a los objetos especificados en mem_objects se hayan completado. Esto se puede realizar de forma portátil emitiendo y esperando la finalización de un comando glFinish en todos los contextos GL con referencias pendientes a estos objetos " –

5

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".

En la actualidad, los controladores 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.

+3

Incorrecto. Como en el comentario que dejé en la respuesta anterior: si está cargando recursos (por ej .: texturas) en un hilo y usándolos para renderizar en otro, con el intercambio a través de wglShareLists o similares, debe llamar a glFinish antes de enviar una señal al hilo de renderizado que es libre de procesar lo que se ha cargado de forma asíncrona. Y no es un no-operativo como dijiste. –

+0

¿De verdad? ¿Puedes respaldar la necesidad de llamar a glFinish antes de usar un objeto compartido con algún enlace de especificación? Puedo ver totalmente un controlador defectuoso que necesita esto. Y ese tipo de vuelve a lo que dije. La gente usa glFinish para evitar los controladores con errores. Debido a que los controladores que funcionan correctamente lo ignoran por el rendimiento. El caso de uso de especificaciones original de creación de perfiles (y representación de almacenamiento único) ya no funciona. – starmole

+2

Experiencia personal de tener que lidiar con texturas dañadas, más esto: http://higherorderfun.com/blog/2011/05/26/multi-thread-opengl-texture-loading/ Si lo piensas bien, tiene sentido , sin embargo, ya que no es muy diferente de tener mutexes u otras sincronizaciones.api between threads: antes de que un contexto pueda usar un recurso compartido, el otro tiene que completar la carga de ese recurso, por lo tanto, la necesidad de garantizar que el buffer haya sido vaciado. Creo que más de esquina de las especificaciones en lugar del error, pero no tengo ninguna prueba para esta declaración :) –

8

Siempre estaba confundido acerca de estos dos comandos también, pero esta imagen hice todo claro para mí: Flush vs Finish Al parecer, algunos controladores de la GPU no enviar los comandos emitidos al hardware a menos que un cierto número de comandos ha sido acumulada . En este ejemplo, ese número es .
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 con glFinish(). Hace casi lo mismo que glFlush(), excepto que hace que el hilo que llama espere hasta que el hardware haya procesado todos los comandos.

Imagen tomada del libro "Programación avanzada de gráficos con OpenGL".

+0

De hecho, sé lo que hacen 'glFlush' y' glFinish', y no puedo decir lo que está diciendo esa imagen. ¿Qué hay en el lado izquierdo y el derecho? Además, ¿esa imagen se publicó en el dominio público o bajo alguna licencia que le permite publicarla en Internet? –

+0

Debería haber elaborado un poco. Acerca de la licencia: en realidad no estoy muy seguro. Me encontré con el PDF en google, mientras investigaba. – Tara