Wie kann ich ein präzises (aber variables) FPS-Limit / Cap in meiner OpenGL-Anwendung implementieren?

8

Ich arbeite gerade an einer OpenGL-Anwendung, um dem Benutzer ein paar 3D-Kugeln zu zeigen, die er drehen, herumbewegen kann, usw. Es gibt hier nicht viel Komplexität, also läuft die Anwendung bei ziemlich hohe Bildrate (~ 500 FPS).

Offensichtlich ist das Overkill - sogar 120 wären mehr als genug, aber mein Problem ist, dass das Ausführen der Anwendung bei Full-Stat meine CPU wegfrisst, was zu Überhitzung, Stromverbrauch usw. führt. Was ich tun möchte, ist Der Benutzer kann eine FPS-Obergrenze festlegen, sodass die CPU nicht zu häufig verwendet wird, wenn sie nicht benötigt wird.

Ich arbeite mit Freeglut und C ++, und habe bereits die Animationen / Event-Handling eingerichtet, um Timer zu verwenden (mit glutTimerFunc ). Das glutTimerFunc erlaubt jedoch nur eine ganzzahlige Anzahl an Millisekunden - wenn ich also 120 FPS haben möchte, ist die nächste% co- de Auflösung, was 125 FPS entspricht (ich weiß, dass es ein vernachlässigbarer Betrag ist, aber Ich möchte nur noch ein FPS-Limit setzen und genau dieses FPS bekommen, wenn ich weiß, dass das System schneller rendern kann.

Außerdem funktioniert die Verwendung von (int)1000/120 = 8 ms zur Begrenzung der FPS nie konsistent. Lassen Sie uns sagen, dass ich meine Anwendung auf 100 FPS kappt, es geht normalerweise nie höher als 90-95 FPS. Wieder habe ich versucht, den Zeitunterschied zwischen Rendering / Berechnungen zu berechnen, aber dann überschreitet es immer das Limit um 5-10 FPS (Timer-Auflösung möglicherweise).

Ich nehme an, der beste Vergleich hier wäre ein Spiel (z. B. Half Life 2) - Sie legen Ihre FPS-Obergrenze fest und sie trifft immer genau diesen Betrag. Ich weiß, dass ich die Zeitdeltas vor und nach dem Rendern jedes Frames messen und dann eine Schleife machen kann, bis ich das nächste zeichnen muss, aber das löst weder mein 100% CPU-Auslastungsproblem, noch löst es das Timing-Problem / p>

Gibt es eine Möglichkeit, einen effektiven, plattformübergreifenden, variablen Frame Rate Limiter / Cap in meiner Anwendung zu implementieren? Oder gibt es auf andere Weise eine plattformübergreifende (und Open-Source-) Bibliothek, die hoch auflösende Timer und Sleep-Funktionen implementiert?

Bearbeiten: Ich würde es vorziehen, eine Lösung zu finden, die nicht darauf angewiesen ist, dass der Endbenutzer VSync aktiviert, da ich die FPS-Obergrenze angeben kann.

Bearbeiten # 2: An alle, die SDL empfehlen (welche Ich habe meine Anwendung letztendlich auf SDL portiert ), gibt es einen Unterschied zwischen der Verwendung der Funktion glutTimerFunc eine Auslosung auslösen oder glutTimerFunc verwenden, um zwischen Auslosungen zu warten? Die Dokumentation für jeden erwähnt die gleichen Vorbehalte, aber ich war mir nicht sicher, ob einer mehr oder weniger effizient als der andere war.

Edit # 3: Im Grunde versuche ich herauszufinden, ob es einen (einfachen) Weg gibt, einen genauen FPS-Limiter in meiner Anwendung zu implementieren (wieder wie Half Life 2). Wenn dies nicht möglich ist, werde ich höchstwahrscheinlich zu SDL wechseln (macht es sinnvoller, eine Verzögerungsfunktion zu verwenden, statt SDL_Delay zu verwenden, um die Renderfunktion alle glutTimerFunc Millisekunden zurückzurufen).

    
Breakthrough 15.07.2011, 13:11
quelle

5 Antworten

1

Ich würde vorschlagen, Sub-ms-Präzisionssystem-Timer (QueryPerformanceCounter, gettimeofday) zu verwenden, um Timing-Daten zu erhalten. Diese können Ihnen auch bei optimierten Release-Builds die Profilleistung verbessern.

    
Steven Lu 01.01.2013, 21:13
quelle
4

Ich würde Ihnen empfehlen, SDL zu verwenden. Ich benutze es persönlich, um meine Timer zu verwalten. Darüber hinaus können Sie Ihre fps auf Ihre Bildschirmaktualisierungsrate (V-Sync) mit SDL 1.3 beschränken. Auf diese Weise können Sie die CPU-Auslastung bei der besten Bildschirmleistung begrenzen (selbst wenn Sie mehr Frames hätten, könnten sie nicht angezeigt werden, da Ihr Bildschirm nicht schnell genug aktualisiert wird).

Die Funktion ist

  

SDL_GL_SetSwapInterval (1);

Wenn Sie Code für Timer mit SDL möchten, können Sie das hier sehen:

meine Timer-Klasse

Viel Glück:)

    
Tuxer 15.07.2011 13:31
quelle
4

Sie sollten nicht versuchen, die Renderrate manuell zu begrenzen, sondern mit der vertikalen Aktualisierung der Anzeige zu synchronisieren. Dies geschieht durch Aktivieren von V-Sync in den Grafiktreibereinstellungen. Abgesehen davon, dass (Ihre) Programme mit hoher Geschwindigkeit rendern, erhöht es auch die Bildqualität, indem es das Reißen vermeidet.

Die Erweiterungen swap interval ermöglichen Ihrer Anwendung eine Feinabstimmung des V-Synchronisierungsverhaltens. Aber in den meisten Fällen aktivieren Sie einfach V-Sync im Treiber und lassen den Puffer so lange blockieren, bis die Synchronisation ausreicht.

    
datenwolf 15.07.2011 13:52
quelle
3

Am einfachsten lösen Sie es, indem Sie Vsync aktivieren. Das mache ich in den meisten Spielen, um zu verhindern, dass mein Laptop zu heiß wird. Solange Sie sicherstellen, dass die Geschwindigkeit Ihres Rendering-Pfads nicht mit der anderen Logik verbunden ist, sollte dies in Ordnung sein.

Es gibt eine Funktion glutGet (GLUT_ELAPSED_TIME), die die Zeit seit dem Start in Millisekunden zurückgibt, aber das ist wahrscheinlich immer noch nicht schnell genug.

Ein einfacher Weg besteht darin, eine eigene Timer-Methode zu erstellen, die den HighPerformanceQueryTimer für Windows und den getTimeOfDay für POSIX-Systeme verwendet.

Oder Sie können immer Timer-Funktionen von SDL oder SFML verwenden, die im Wesentlichen die gleichen Funktionen wie oben haben.

    
Marking 15.07.2011 13:33
quelle
3

Ich denke, ein guter Weg, um dies zu erreichen, ist, unabhängig davon, welche Grafikbibliothek Sie verwenden, eine einzige Taktmessung in der Gameloop, um jeden einzelnen Tick (ms) zu berücksichtigen. Auf diese Weise werden die durchschnittlichen fps genauso wie in Half-Life 2 genau das Limit sein. Hoffentlich wird das folgende Code-Snippet erklären, worüber ich spreche:

%Vor%

Ich hoffe, dieses Beispiel mit SDL hilft. Es ist wichtig, die Zeit nur einmal pro Rahmen zu messen, damit jeder Rahmen berücksichtigt wird. Ich empfehle, dieses Timing in einer Funktion zu modularisieren, die auch Ihren Code klarer macht. Dieser Code hat keine Kommentare für den Fall, dass sie dich in der letzten nur genervt haben:

%Vor%

Jetzt können Sie diese Funktion in Ihrem Hauptknoten anstatt Ihrer swapBuffer-Funktion aufrufen ( SDL_RenderPresent(renderer) in SDL). In SDL müssen Sie sicherstellen, dass das Flag SDL_RENDERER_PRESENTVSYNC deaktiviert ist. Diese Funktion basiert auf der globalen Variablen FPS , aber Sie können sich andere Speichermöglichkeiten vorstellen. Ich lege das Ganze einfach in den Namensraum meiner Bibliothek.

Diese Methode, die Framerate zu capping, liefert genau die gewünschte durchschnittliche Framerate, wenn es keine großen Unterschiede in der Looptime über mehrere Frames gibt, da 30ms auf deltaticks beschränkt sind. Das deltaticks -Grenze ist erforderlich. Wenn das FPS-Limit höher als die tatsächliche Framerate ist, wird deltaticks auf unbestimmte Zeit fallen. Auch wenn die Framerate dann wieder über das FPS-Limit ansteigt, würde der Code versuchen, die verlorene Zeit zu kompensieren, indem jeder Frame sofort gerendert wird, was zu einer großen Framerate führt, bis deltaticks wieder auf Null ansteigt. Sie können die 30ms an Ihre Bedürfnisse anpassen, es ist nur eine Schätzung von mir. Ich habe ein paar Benchmarks mit Fraps gemacht. Es funktioniert wirklich mit jeder erdenklichen Framerate und liefert schöne Ergebnisse von dem, was ich getestet habe.

Ich muss zugeben, dass ich das erst gestern geschrieben habe, also ist es nicht unwahrscheinlich, dass es einen Bug gibt. Ich weiß, dass diese Frage vor 5 Jahren gestellt wurde, aber die gegebenen Antworten haben mich nicht bestätigt. Fühlen Sie sich auch frei, diesen Beitrag zu bearbeiten, da es meiner allerersten und wahrscheinlich fehlerhaft ist.

BEARBEITEN: Es wurde mir mitgeteilt, dass SDL_Delay auf manchen Systemen sehr ungenau ist. Ich habe einen Fall gehört, in dem es sich bei Android viel zu sehr verzögert hat. Dies bedeutet, dass mein Code möglicherweise nicht auf alle gewünschten Systeme übertragbar ist.

    
Neop 22.09.2016 15:04
quelle