2014-09-19 17 views
5

Ho un semplice ciclo di gioco utilizzando GLFW come segue (le finestre in modalità x64 Release)Perché il semplice programma glfw mangia tutte le CPU disponibili anche se il programma è inattivo (secondo Process Explorer)?

mi aspetterei il programma da eseguire molto rapidamente, ma a quanto pare il mio delta come calcolato sotto è sempre 16.667ms che sembrerebbe che glfw è in qualche modo limitando la velocità del mio ciclo principale. Questo non è un problema perché non mi interessa ottenere più di 60hz. Tuttavia, Process Explorer e Windows Task Manager segnalano che il mio programma utilizza la maggior parte del core della CPU.

In particolare, mangiare sembra che glfwSwapBuffers() mangi molta CPU, anche se non sto disegnando nulla. La rimozione di tale chiamata riduce l'utilizzo della CPU allo 0,5%.

Incidentalmente la mia funzione Sleep non viene quasi mai chiamata perché il delta è sempre vicino esattamente a 16,6ms.

main() 
{ 
    double prevTime = glfwGetTime(); 
    //init glfw .. 
    while(!glfwWindowShouldClose(window)) 
    { 
     double time0=glfwGetTime(); 
     double delta = time0- prevTime; 

     if (delta >= g_FrameInterval) 
     { 
      glfwPollEvents(); 
      prevTime= time0; 
      glfwSwapBuffers(window); 
     } 
     else 
     { 
      Sleep(10); 
     } 
    } 
} 
+0

Probabilmente dovresti aggiungere il tag 'Windows', poiché la tua domanda è specifica di Windows. –

risposta

3

glfwSwapBuffers è in attesa del monitor vsync. Questo è il motivo per cui il tuo loop funziona a 60 Hz (ovvero la frequenza della frequenza di aggiornamento del monitor). Per quanto riguarda l'alta CPU, il sistema operativo probabilmente non mette il processo in stop. Ciò probabilmente causerebbe la perdita del vsync perché non può riattivarsi abbastanza velocemente. Invece, la CPU viene messa in un ciclo occupato fino al vsync. Ecco un numero more full explanation del problema.

+1

ahh interessante. Questo ha senso. Ho appena scoperto dopo aver postato che glfwSwapBuffers sembra essere bloccato e che l'impostazione glfwSwapInterval (0); si ferma e con drasticamente diminuisce drasticamente l'uso della cpu riportato da Windows. Suppongo che lo svantaggio di disabilitarlo e di usare il mio sonno si stia lacerando. – skimon

2

Sembra che sia necessario sincronizzare il thread in base a quando vengono restituiti i buffer di scambio. Effettua un paio di chiamate buffer "fittizie" (con una schermata di avvio), leggendo un timer dopo ogni chiamata per ottenere la frequenza (potrebbe essere 120 hz su alcuni monitor, o se un vecchio monitor CRT, 60hz, 75hz, 85hz, 100hz, 120hz, 160hz, 200hz) e per impostare un conteggio del timer iniziale.

Se è ok solo per eseguire la velocità di monitoraggio, è possibile utilizzare un valore di Sleep() fisso ipotizzando un sovraccarico massimo per il codice (a seconda del sistema di destinazione più lento possibile). La frequenza di tick predefinita per Windows è 64hz (15.625 ms), ma può essere accelerata usando timeBeginPeriod (1), nel qual caso Sleep (n) richiede circa n ms su Windows 7 o successivi, ma fino a n + 1 ms su Windows XP. Ad esempio, se il tuo codice ha bisogno di meno di 5 ms di tempo CPU per ciascun frame, a 60hz potresti usare solo un Sleep (10) fisso (o Sleep (9) se Windows XP) dopo ogni chiamata ai buffer di swap, o se a 120 hz, quindi Sleep (2) (o Sleep (1) se Windows XP).

Molti giochi utilizzano una filettatura separata per la fisica che funziona a una frequenza fissa non correlata alla frequenza video. Ecco un esempio di ciò senza alcuna deriva nel tempo (il delta si basa su una lettura originale di un orologio ad alta frequenza). Dovrebbe essere in un thread separato dal thread grafico e segnalare il thread della grafica ogni volta che un aggiornamento del frame è pronto (mutex, semaforo, qualche tipo di funzione di messaggistica).

/* code for a thread to run at fixed frequency */ 
typedef unsigned long long UI64;  /* unsigned 64 bit int */ 
#define FREQ 400      /* frequency */ 

LARGE_INTEGER liPerfTemp;    /* used for query */ 
UI64 uFreq = FREQ;      /* process frequency */ 
UI64 uOrig;        /* original tick */ 
UI64 uWait;        /* tick rate/freq */ 
UI64 uRem = 0;       /* tick rate % freq */ 
UI64 uPrev;        /* previous tick based on original tick */ 
UI64 uDelta;       /* current tick - previous */ 
UI64 u2ms;        /* 2ms of ticks */ 
UI64 i; 

    /* ... */ /* wait for some event to start thread */ 
    timeBeginPeriod(1);     /* set period to 1ms */ 
    Sleep(128);       /* wait for it to stabilize */ 

    u2ms = ((UI64)(liPerfFreq.QuadPart)+499)/((UI64)500); 

    QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp); 
    uOrig = uPrev = liPerfTemp.QuadPart; 

    for(i = 0; i < (uFreq*30); i++){ 
     /* update uWait and uRem based on uRem */ 
     uWait = ((UI64)(liPerfFreq.QuadPart) + uRem)/uFreq; 
     uRem = ((UI64)(liPerfFreq.QuadPart) + uRem) % uFreq; 
     /* wait for uWait ticks */ 
     while(1){ 
      QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp); 
      uDelta = (UI64)(liPerfTemp.QuadPart - uPrev); 
      if(uDelta >= uWait) 
       break; 
      if((uWait - uDelta) > u2ms) 
       Sleep(1); 
     } 
     if(uDelta >= (uWait*2)) 
      dwLateStep += 1; 
     uPrev += uWait; 
     /* fixed frequency code goes here */ 
     /* along with some type of break when done */ 
    } 

    timeEndPeriod(1);     /* restore period */ 
Problemi correlati