Tengo una aplicación de prueba de OpenGL simple en C que dibuja cosas diferentes en respuesta a la entrada clave. (Mesa 8.0.4, probado con Mesa-EGL y con GLFW, Ubuntu 12.04LTS en una PC con NVIDIA GTX650). Los sorteos son bastante simples / rápidos (tipo de cosas de triángulo giratorio). Mi código de prueba no limita la velocidad de fotogramas deliberadamente de ninguna manera, simplemente se ve así:
while (true)
{
draw();
swap_buffers();
}
He cronometrado esto con mucho cuidado, y encuentro que el tiempo de una eglSwapBuffers()
(o la glfwSwapBuffers
misma) llamada a la siguiente es ~ 16.6 milisegundos. El tiempo desde después de una llamada hasta eglSwapBuffers()
justo antes de la próxima llamada es solo un poco menor que eso, aunque lo que se dibuja es muy simple. El tiempo que demora la llamada de las memorias intermedias de intercambio es muy inferior a 1 ms.
Sin embargo, el tiempo desde que la aplicación cambia lo que está dibujando en respuesta a la pulsación de la tecla al cambio que aparece en la pantalla es> 150 ms (aproximadamente 8-9 cuadros). Esto se mide con una grabación de la cámara de la pantalla y el teclado a 60 fps.
Por lo tanto, las preguntas:
¿Dónde se almacenan los sorteos entre una llamada para intercambiar buffers y aparecer en la pantalla? ¿Por qué la demora? Parece que la aplicación está dibujando muchos cuadros por delante de la pantalla en todo momento.
¿Qué puede hacer una aplicación OpenGL para provocar un sorteo inmediato en la pantalla? (es decir, sin almacenamiento en búfer, solo bloquee hasta que se complete el dibujo; no necesito un alto rendimiento, necesito una baja latencia)
¿Qué puede hacer una aplicación para que el sorteo inmediato anterior suceda lo más rápido posible?
¿Cómo puede una aplicación saber qué está realmente en la pantalla en este momento? (O, ¿cuánto tiempo / cuántos cuadros tiene el retraso de almacenamiento en búfer actual?)
Respuestas:
Cualquier función API de dibujo llamada desde la CPU se enviará al búfer de anillo de comando de la GPU para que la GPU la ejecute más tarde. Esto significa que las funciones de OpenGL son principalmente funciones sin bloqueo. Entonces la CPU y la GPU trabajarán en paralelo.
Lo más importante a tener en cuenta es que su aplicación puede estar vinculada a la CPU o GPU. una vez que llame a glFinish, la CPU debería esperar a que la GPU complete sus comandos de dibujo, si la GPU está tomando más tiempo y puede / está causando que la CPU se detenga, entonces sus aplicaciones están vinculadas a la GPU. Si la GPU termina de dibujar comandos y la CPU está tardando demasiado en terminar, entonces su aplicación está vinculada a la CPU.
Y tenga en cuenta que hay una diferencia entre
glFlush
yglFinish
.glFlush
: indica que todos los comandos que se han enviado previamente al GL deben completarse en tiempo finito.glFinish
: obliga a completar todos los comandos GL anteriores. "Finalizar no regresa hasta que todos los efectos de los comandos emitidos previamente en el cliente GL y el estado del servidor y el framebuffer se realicen completamente".glXSwapBuffers realiza un glFlush implícito antes de que regrese. Los comandos subsiguientes de OpenGL pueden emitirse inmediatamente después de llamar a glXSwapBuffers, pero no se ejecutan hasta que se completa el intercambio del búfer.
El tiempo de trama real probablemente estará determinado por cuál de las dos CPU / GPU está tomando más tiempo para completar su trabajo.
fuente
both low framerate (exactly same as monitor refresh rate??
no a menos que esté utilizando explícitamente VSync.OpenGL nunca actualiza la pantalla, técnicamente.
Hay una API del sistema de ventanas que está separada de GL (por ejemplo, GLX, WGL, CGL, EGL) que hace esto. Los intercambios de búfer que usan estas API generalmente invocan implícitamente,
glFlush (...)
pero en algunas implementaciones (por ejemplo, el rasterizador GDI en Windows) hace un completoglFinish (...)
:* En el lado izquierdo, se encuentra la ruta del ICD (hardware)
SwapBuffers (...)
. En el lado derecho, la ruta GDI (software).Si tiene VSYNC habilitado y doble búfer, entonces cualquier comando que modifique el búfer posterior antes de que ocurra un intercambio pendiente debe detenerse hasta el intercambio. La cola de comandos tiene una profundidad limitada, por lo que este comando bloqueado eventualmente provocará un atasco de tráfico en la tubería. Esto podría significar que, en lugar de bloquear
SwapBuffers (...)
su aplicación, en realidad bloquea algunos comandos GL no relacionados hasta que VBLANK llegue. Lo que realmente se reduce a cuántos buffers de respaldo tiene en su cadena de intercambio.Siempre y cuando todos los buffers posteriores estén llenos de marcos terminados que aún no se hayan movido al frente, los buffers de intercambio provocarán implícitamente el bloqueo. Lamentablemente, no hay forma de controlar explícitamente el número de buffers de respaldo utilizados por la mayoría de las API del sistema de ventanas GL (aparte de 0 de un solo buffer o 1 de doble buffer). El controlador es libre de usar 2 buffers posteriores si lo desea (triple buffering), pero no puede solicitarlo a nivel de aplicación usando algo como GLX o WGL.
fuente
glFinish (...)
inmediatamente después de intercambiar buffers. Esto borrará la cola de comandos, pero también significa que la GPU y la CPU se sincronizarán (lo que no es bueno si desea que la GPU funcione en todo momento).¿Asumo que estás familiarizado con este experimento ?
Esencialmente, John Carmack estaba haciendo algo similar, grabando la pantalla y sincronizando los píxeles enviados a la pantalla. Descubrió que buena parte de la latencia provenía de la pantalla. Otros factores fueron el retraso de entrada del teclado, los controladores de video y, por supuesto, la ejecución del programa en sí.
fuente