2014-09-19 6 views
5

Ich habe ein sehr einfaches Spiel Schleife GLFW wie folgt verwendet wird (Windows x64 Release-Modus)Warum isst einfaches GLFW-Programm die gesamte verfügbare CPU, obwohl das Programm im Leerlauf ist (laut Process Explorer)?

ich das Programm erwarten würde sehr schnell auszuführen, scheint aber es ist mein Delta wie unten immer berechnet wird 16.667ms welche diese GLFW erscheinen würde ist irgendwie die Geschwindigkeit meiner Hauptschleife zu begrenzen. Dies ist kein Problem, da es mir egal ist, mehr als 60hz zu bekommen. Prozess-Explorer und Windows-Task-Manager berichten jedoch, dass mein Programm den Großteil des CPU-Kerns verwendet.

Speziell essen scheint, dass glfwSwapBuffers() eine Menge CPU isst, obwohl ich nichts zeichne. Wenn Sie diesen Anruf entfernen, sinkt die CPU-Nutzung auf 0,5%.

Übrigens wird meine Schlaffunktion fast nie aufgerufen, weil das Delta immer genau 16.6ms ist.

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

Sie sollten wahrscheinlich das 'Windows'-Tag hinzufügen, da Ihre Frage spezifisch für Windows ist. –

Antwort

3

glfwSwapBuffers wartet auf den Monitor vsync. Aus diesem Grund läuft die Schleife mit 60 Hz (das ist die Frequenz der Bildwiederholfrequenz Ihres Monitors). Was die hohe CPU anbelangt, schaltet das Betriebssystem Ihren Prozess wahrscheinlich nicht in den Ruhezustand. Dies würde wahrscheinlich dazu führen, dass es den vsync vermisst, weil es nicht schnell genug wieder aufwachen kann. Stattdessen wird die CPU bis zum Vsync in eine Busy-Schleife versetzt. Hier ist eine more full explanation des Problems.

+1

ahh interessant. Das macht Sinn. Ich habe gerade nach der Veröffentlichung entdeckt, dass glfwSwapBuffers blockiert zu sein scheint und dass glfwSwapInterval (0); Stoppt dies und reduziert drastisch die von Windows gemeldete CPU-Nutzung. Ich nehme an, der Nachteil, dies zu verhindern und meinen Schlaf zu nutzen, wird reißen. – skimon

2

Es scheint, dass Sie Ihren Thread basierend darauf synchronisieren müssen, wann Swap-Puffer zurückgegeben werden. Machen Sie ein paar "Dummy" Swap-Puffer-Aufrufe (mit einem Start-Bildschirm), Lesen eines Timers nach jedem Anruf, um die Frequenzrate zu erhalten (es könnte 120 Hz auf einigen Monitoren sein, oder wenn ein alter CRT-Monitor, 60Hz, 75Hz, 85hz, 100hz, 120hz, 160hz, 200hz) und um einen anfänglichen Timer Count zu setzen.

Wenn es in Ordnung ist, nur mit der Überwachungsrate zu arbeiten, dann könnten Sie einen festen Sleep() -Wert verwenden, unter der Annahme eines maximalen Aufwands für Ihren Code (abhängig vom langsamsten Zielsystem). Die Standard-Tick-Rate für Windows ist 64hz (15,625 ms), aber dies kann mit timeBeginPeriod (1) beschleunigt werden. In diesem Fall dauert Sleep (n) unter Windows 7 oder höher etwa n ms, aber bis zu n + 1 ms Windows XP. Wenn Ihr Code zum Beispiel weniger als 5 ms CPU-Zeit für jeden Frame benötigt, können Sie bei 60hz einfach einen festen Schlafmodus (10) (oder Sleep (9) wenn Windows XP) nach jedem Aufruf eines Pufferaustauschs verwenden bei 120Hz, dann Sleep (2) (oder Sleep (1) wenn Windows XP).

Viele Spiele verwenden einen separaten Thread für die Physik, der mit einer festen Frequenz unabhängig von der Videofrequenz ausgeführt wird. Hier ist ein Beispiel dafür, ohne mit der Zeit zu driften (das Delta basiert auf einem ursprünglichen Lesen eines Hochfrequenztakts). Es würde in einem separaten Thread von dem Grafik-Thread sein und den Graphik-Thread signalisieren, wann immer eine Frame-Aktualisierung bereit ist (Mutex, Semaphor, irgendeine Art von Messaging-Funktion).

/* 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 */