c++ - installing - Evite esperar en SwapBuffers
opengl ejemplos c++ (2)
Descubrí que SwapBuffers en OpenGL estará ocupado, espera siempre y cuando la tarjeta gráfica no haya terminado con su renderización o si está esperando en V-Sync.
Este es un problema para mí porque no quiero desperdiciar el 100% del núcleo de la CPU mientras espero a que se termine la tarjeta. No estoy escribiendo un juego, así que no puedo usar los ciclos de CPU para algo más productivo, solo quiero cederlos a algún otro proceso en el sistema operativo.
He encontrado funciones de devolución de llamada como glutTimerFunc y glutIdleFunc que podrían funcionar para mí, pero no quiero usar glut. Sin embargo, glut debe usar de alguna manera las funciones gl normales para hacer esto, ¿verdad?
¿Existe alguna función como "glReadyToSwap" o similar? En ese caso, podría comprobar cada milisegundo o más y determinar si debo esperar un poco más o hacer el cambio. También podría imaginarme tal vez omitir SwapBuffers y escribir mi propia función similar que no esté ocupada; espere si alguien podría apuntarme en la dirección correcta.
A pesar de que eglSwapBuffers
no está ocupado, un uso legítimo de un eglSwapBuffers
bloqueo es tener un hilo GUI más sensible que pueda escuchar las entradas del usuario o salir en lugar de esperar a que OpenGL termine de intercambiar los buffers. Tengo una solución a la mitad de este problema. Primero, en su bucle principal, almacena sus comandos OpenGL para ejecutarlos en su búfer de intercambio. Luego realiza una encuesta en un objeto de sincronización para ver si sus comandos han terminado de ejecutarse en su búfer de intercambio. Luego puedes intercambiar buffers si los comandos han terminado de ejecutarse. Desafortunadamente, esta solución solo espera de forma asíncrona a que los comandos terminen de ejecutarse en su búfer de intercambio y no espera de forma asíncrona a vsync. Aquí está el código:
void process_gpu_stuff(struct gpu_context *gpu_context)
{
int errnum = 0;
switch (gpu_context->state) {
case BUFFER_COMMANDS:
glDeleteSync(gpu_context->sync_object);
gpu_context->sync_object = 0;
real_draw(gpu_context);
glFlush();
gpu_context->sync_object = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
if (0 == gpu_context->sync_object) {
errnum = get_gl_error();
break;
}
gpu_context->state = SWAP_BUFFERS;
break;
case SWAP_BUFFERS:
/* Poll to see if the buffer is ready for swapping, if
* it is not in ready we can listen for updates in the
* meanwhile. */
switch (glClientWaitSync(gpu_context->sync_object, 0, 1000U)) {
case GL_ALREADY_SIGNALED:
case GL_CONDITION_SATISFIED:
if (EGL_FALSE == eglSwapBuffers(display, surface)) {
errnum = get_egl_error();
break;
}
gpu_context->state = BUFFER_COMMANDS;
break;
case GL_TIMEOUT_EXPIRED:
/* Do nothing. */
break;
case GL_WAIT_FAILED:
errnum = get_gl_error();
break;
}
break;
}
}
SwapBuffers no está ocupado esperando , simplemente bloquea su hilo en el contexto del controlador, lo que hace que Windows calcule incorrectamente el uso de la CPU: Windows calcula el uso de la CPU al determinar cuánto tiempo de CPU obtiene el proceso inactivo + cuánto tiempo no pasan los programas Contexto del conductor. SwapBuffers se bloqueará en el contexto del controlador y su programa obviamente le quita ese tiempo de CPU al proceso inactivo. Pero su CPU no está haciendo literalmente nada en el tiempo, el programador felizmente espera pasar el tiempo a otros procesos. El proceso inactivo OTOH no hace nada más que entregar de inmediato su tiempo al resto del sistema, por lo que el programador vuelve a su proceso, lo que bloquea en el controlador lo que Windows cuenta como "está atascando la CPU". Si midiera el consumo de energía real o la salida de calor, para un programa OpenGL simple, esto se mantendrá bastante bajo.
¡Este comportamiento irritante es en realidad una FAQ de OpenGL!
Simplemente cree hilos adicionales para el procesamiento de datos en paralelo. Mantenga OpenGL en un hilo, el procesamiento de datos en el otro. Si desea reducir el uso de CPU informado, agregue un Sleep (0) o un Sleep (1) después de que SwapBuffers haga el truco. El modo de reposo (1) hará que su proceso invierta un poco de tiempo en el contexto del usuario, por lo que el proceso inactivo obtiene más tiempo, lo que igualará los números. Si no quieres dormir, puedes hacer lo siguiente:
const float time_margin = ... // some margin
float display_refresh_period; // something like 1./60. or so.
void render(){
float rendertime_start = get_time();
render_scene();
glFinish();
float rendertime_finish = get_time();
float time_to_finish = rendertime_finish - rendertime_start;
float time_rest = fmod(render_finish - time_margin, display_refresh_period);
sleep(time_rest);
SwapBuffers();
}
En mis programas, utilizo este tipo de tiempo, pero por otra razón: dejo que SwapBuffers bloquee sin ningún tipo de ayuda Duerme, sin embargo, le doy a otros hilos de trabajo ese tiempo para hacer cosas en la GPU a través de un contexto compartido (como actualizar texturas) y tengo el recolector de basura corriendo. Realmente no es necesario cronometrarlo exactamente, pero los subprocesos de trabajo que se terminan justo antes de que SwapBuffers regrese le permiten comenzar a renderizar el siguiente fotograma casi inmediatamente, ya que la mayoría de los mutexs ya están desbloqueados.