2011-04-29 9 views
18

He descubierto que SwapBuffers en OpenGL estará ocupado, espere mientras la tarjeta gráfica no esté lista con su renderizado o si está esperando en V-Sync.Evitar esperar en SwapBuffers

Esto es un problema para mí porque no quiero desperdiciar el 100% de un núcleo de CPU mientras espero que la tarjeta se termine. 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 utilizar el exceso. Aún así, el exceso debe de alguna manera usar las funciones gl normales para hacer esto, ¿verdad?

¿Hay alguna función como "glReadyToSwap" o similar? En ese caso, podría verificarlo cada milisegundo y determinar si debería esperar un poco más o realizar el intercambio. También podría imaginar saltar SkapBuffers y escribir mi propia función similar que no está ocupada, esperar si alguien puede señalarme en la dirección correcta.

Respuesta

22

SwapBuffers no está ocupado esperando, sólo se bloquea el hilo en el contexto del conductor, lo que hace que Windows calcular el uso de la CPU erróneamente: Windows calcula el uso de la CPU mediante la determinación de la cantidad de tiempo de CPU del proceso inactivo obtiene + la cantidad de tiempo los programas no se gastan en el contexto del conductor. SwapBuffers se bloqueará en el contexto del controlador y su programa, obviamente, le quita ese tiempo de CPU del proceso inactivo. Pero su CPU no está haciendo literalmente nada en el tiempo, el programador está felizmente esperando pasar el tiempo a otros procesos. El proceso inactivo OTOH no hace nada más que ceder inmediatamente su tiempo al resto del sistema, por lo que el planificador salta directamente a su proceso, lo que bloquea en el controlador lo que Windows cuenta como "está obstruyendo la CPU". Si mides el consumo real de potencia o la potencia térmica, para un programa OpenGL simple, esto se mantendrá bastante bajo.

¡Este comportamiento irritante es realmente una pregunta frecuente de OpenGL!

Simplemente cree subprocesos adicionales para el procesamiento paralelo de datos. Mantenga OpenGL en un hilo, el procesamiento de datos en el otro. Si desea bajar el uso de CPU reportado, agregando un modo de suspensión (0) o suspensión (1) después de, los intercambiadores de intercambio harán el truco. The Sleep (1) hará que el proceso pase bloqueando un poco el tiempo en el contexto del usuario, por lo que el proceso inactivo tendrá más tiempo, lo que igualará los números. Si no desea dormir, puede 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 que utilizo este tipo de tiempo, pero por otra razón: dejo que bloquean SwapBuffers sin ningún ayudante de Camas, sin embargo, me doy alguna otra Trabajador conversaciones sobre ese momento para hacer cosas en la GPU a través del contexto compartido (como la actualización de texturas) y tengo el recolector de basura en ejecución. No es realmente necesario medir exactamente el tiempo, pero los subprocesos de trabajo terminados justo antes de que SwapBuffers regrese permiten comenzar a renderizar el siguiente fotograma casi inmediatamente ya que la mayoría de los mutex ya están desbloqueados.

+0

Bueno, los detalles sobre cómo ocurre la espera no son tan importantes para mí. Lo que puedo decir es que los fanáticos en la computadora aceleran, y supongo que reaccionan con el calor, lo que significa que la CPU FUNCIONA al 100% ya que SE CALIENTA. Incluso si no fuera así, Windows cree que funciona al 100%, lo que significa que Windows no dará tiempo de CPU a ningún otro proceso porque cree que ya se utilizó al 100%. Como sea que lo mires, desperdicias tiempo de CPU que podría ser usado por otra persona ya sea quemándolo o dejándolo funcionar. – DaedalusAlpha

+0

¿Intentó ejecutar un programa diferente que también consume mucho tiempo de CPU? – datenwolf

+0

^^ Me refería a su programa OpenGL. – datenwolf

0

Aunque eglSwapBuffers no espera ocupado un uso legítimo para un bloqueante eglSwapBuffers es tener un hilo de interfaz gráfica de usuario más sensible que puede escuchar las señales de entrada o salida de usuario en lugar de esperar a que OpenGL para terminar intercambiando memorias intermedias. Tengo una solución para la mitad de este problema. Primero en su bucle principal, almacena sus comandos OpenGL en búfer para ejecutarlos en su búfer intercambiado. Luego sondeas un objeto de sincronización para ver si tus comandos han terminado de ejecutarse en tu buffer intercambiado. Luego puedes intercambiar búferes si los comandos han terminado de ejecutarse. Desafortunadamente, esta solución solo espera asíncronamente que los comandos terminen de ejecutarse en su búfer intercambiado y no espera asincrónicamente 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; 
    } 
} 
Cuestiones relacionadas