web-dev-qa-db-de.com

opengl: glFlush () vs. glFinish ()

Ich habe Probleme, den praktischen Unterschied zwischen dem Aufrufen von glFlush() und glFinish() zu erkennen.

Die Dokumente besagen, dass glFlush() und glFinish() alle gepufferten Operationen an OpenGL senden, damit sichergestellt ist, dass sie alle ausgeführt werden. Der Unterschied besteht darin, dass glFlush() zurückgegeben wird sofort, wo als glFinish() blockiert, bis alle Operationen abgeschlossen sind.

Nachdem ich die Definitionen gelesen hatte, stellte ich fest, dass ich bei der Verwendung von glFlush() wahrscheinlich das Problem bekommen würde, mehr Operationen an OpenGL zu senden, als ausgeführt werden könnten. Also, nur um es zu versuchen, habe ich meine glFinish() gegen eine glFlush() getauscht und siehe da, mein Programm lief (soweit ich das beurteilen konnte), genauso; Frameraten, Ressourcenverbrauch, alles war gleich.

Ich frage mich also, ob zwischen den beiden Aufrufen ein großer Unterschied besteht oder ob sie aufgrund meines Codes nicht unterschiedlich ausgeführt werden. Oder wo sollte man gegen die anderen verwendet werden. Ich dachte mir auch, dass OpenGL einen Aufruf wie glIsDone() haben würde, um zu prüfen, ob alle gepufferten Befehle für eine glFlush() vollständig sind oder nicht (man sendet also keine Operationen schneller an OpenGL) als sie ausgeführt werden können), aber ich konnte keine solche Funktion finden.

Mein Code ist die typische Spieleschleife:

while (running) {
    process_stuff();
    render_stuff();
}
101
jay.lee

Beachten Sie, dass diese Befehle seit den Anfängen von OpenGL existieren. glFlush stellt sicher, dass frühere OpenGL-Befehle in endlicher Zeit ausgeführt werden müssen ( OpenGL 2.1-Spezifikationen , Seite 245). Wenn Sie direkt in den Frontbuffer zeichnen, muss dies sicherstellen, dass die OpenGL-Treiber unverzüglich mit dem Zeichnen beginnen. Sie können sich eine komplexe Szene vorstellen, die Objekt für Objekt auf dem Bildschirm angezeigt wird, wenn Sie nach jedem Objekt glFlush aufrufen. Bei Verwendung der doppelten Pufferung hat glFlush jedoch praktisch keine Auswirkung, da die Änderungen erst sichtbar werden, wenn Sie die Puffer austauschen.

glFinish kehrt erst zurück, wenn alle Effekte von zuvor ausgegebenen Befehlen [...] vollständig realisiert sind . Dies bedeutet, dass die Ausführung Ihres Programms hier wartet, bis jedes letzte Pixel gezeichnet ist und OpenGL nichts mehr zu tun hat. Wenn Sie direkt in den Frontbuffer rendern, müssen Sie zunächst glFinish aufrufen, bevor Sie mithilfe der Betriebssystemaufrufe Screenshots erstellen. Es ist weitaus weniger nützlich für die doppelte Pufferung, da Sie die Änderungen, zu deren Durchführung Sie gezwungen sind, nicht sehen.

Wenn Sie also die doppelte Pufferung verwenden, benötigen Sie wahrscheinlich weder glFlush noch glFinish. SwapBuffers leitet die OpenGL-Aufrufe implizit an den richtigen Puffer weiter glFlush muss nicht zuerst aufgerufen werden . Und es macht nichts aus, den OpenGL-Treiber zu betonen: glFlush verschluckt sich nicht an zu vielen Befehlen. Es ist nicht garantiert, dass dieser Aufruf sofort zurückgibt (was auch immer das bedeutet), so dass es Zeit in Anspruch nehmen kann, Ihre Befehle zu verarbeiten.

90
Malte Clasen

Wie die anderen Antworten andeuteten, gibt es wirklich keine gute Antwort gemäß der Spezifikation. Die allgemeine Absicht von glFlush() ist, dass die Host-CPU nach dem Aufruf keine OpenGL-bezogene Arbeit mehr ausführen muss - die Befehle wurden an die Grafikhardware weitergeleitet. Die allgemeine Absicht von glFinish() ist, dass nach der Rückkehr no die verbleibende Arbeit übrig bleibt und die Ergebnisse auch für alle geeigneten Nicht-OpenGL-APIs verfügbar sein sollten (z. B. Lesevorgänge aus dem Framebuffer). Screenshots, etc ...). Ob das wirklich passiert, ist fahrerabhängig. Die Spezifikation erlaubt eine Menge Spielraum, was legal ist.

21
Andy Ross

Ich war auch immer verwirrt über diese beiden Befehle, aber dieses Bild machte mir alles klar: Flush vs Finish Anscheinend senden einige GPU-Treiber die ausgegebenen Befehle nur an die Hardware, wenn eine bestimmte Anzahl von Befehlen gesammelt wurde. In diesem Beispiel ist diese Zahl 5 .
Das Bild zeigt verschiedene OpenGL-Befehle (A, B, C, D, E ...), die ausgegeben wurden. Wie wir oben sehen können, werden die Befehle noch nicht ausgegeben, da die Warteschlange noch nicht voll ist.

In der Mitte sehen wir, wie sich glFlush() auf die Befehle in der Warteschlange auswirkt. Der Treiber wird angewiesen, alle in der Warteschlange befindlichen Befehle an die Hardware zu senden (auch wenn die Warteschlange noch nicht voll ist). Dies blockiert den aufrufenden Thread nicht. Es signalisiert dem Fahrer lediglich, dass wir möglicherweise keine zusätzlichen Befehle senden. Daher wäre es Zeitverschwendung, darauf zu warten, dass sich die Warteschlange füllt.

Unten sehen wir ein Beispiel mit glFinish(). Es macht fast das Gleiche wie glFlush(), außer dass der aufrufende Thread wartet, bis alle Befehle von der Hardware verarbeitet wurden.

Bild aus dem Buch "Advanced Graphics Programming Using OpenGL".

14
Tara

Wenn Sie keinen Leistungsunterschied feststellen, bedeutet dies, dass Sie etwas falsch gemacht haben. Wie einige andere erwähnt haben, müssen Sie auch nicht anrufen. Wenn Sie jedoch glFinish aufrufen, verlieren Sie automatisch die Parallelität, die die GPU und die CPU erreichen können. Lass mich tiefer eintauchen:

In der Praxis werden alle Arbeiten, die Sie an den Treiber senden, gestapelt und möglicherweise viel später (z. B. zur SwapBuffer-Zeit) an die Hardware gesendet.

Wenn Sie also glFinish aufrufen, müssen Sie den Treiber im Wesentlichen dazu zwingen, die Befehle an die GPU zu senden (die bis dahin gestapelt und die GPU nie zur Arbeit aufgefordert hat) und die CPU anzuhalten, bis die gesendeten Befehle vollständig sind hingerichtet. Während der gesamten Zeit, in der die GPU funktioniert, funktioniert die CPU nicht (zumindest in diesem Thread). Und die ganze Zeit, in der die CPU ihre Arbeit erledigt (hauptsächlich Stapelbefehle), macht die GPU nichts. Also ja, glFinish sollte deine Leistung beeinträchtigen. (Dies ist eine Annäherung, da die GPU bei einigen Befehlen möglicherweise in Betrieb genommen wird, wenn viele bereits gestapelt wurden. Dies ist jedoch nicht typisch, da die Befehlspuffer in der Regel groß genug sind, um viele Befehle zu speichern.).

Warum würden Sie dann glFinish überhaupt anrufen? Ich habe es nur verwendet, als ich Treiberfehler hatte. Wenn einer der Befehle, die Sie an die Hardware senden, die GPU zum Absturz bringt, können Sie am einfachsten feststellen, welcher Befehl der Schuldige ist, indem Sie nach jedem Draw glFinish aufrufen. Auf diese Weise können Sie eingrenzen, was genau den Absturz auslöst

Nebenbei bemerkt, APIs wie Direct3D unterstützen überhaupt kein Finish-Konzept.

6
Bahbar

glFlush stammt tatsächlich aus einem Client-Server-Modell. Sie senden alle gl-Befehle über eine Pipe an einen gl-Server. Diese Pipe könnte puffern. Genau wie bei jeder Datei oder Netzwerk-E/A, die gepuffert werden kann. glFlush sagt nur "Sende den Puffer jetzt, auch wenn er noch nicht voll ist!". Auf einem lokalen System wird dies so gut wie nie benötigt, da eine lokale OpenGL-API sich wahrscheinlich nicht selbst puffert und nur Befehle direkt ausgibt. Auch alle Befehle, die das eigentliche Rendern verursachen, führen eine implizite Leerung durch.

glFinish wurde dagegen zur Leistungsmessung entwickelt. Eine Art PING an den GL Server. Er rundet einen Befehl ab und wartet, bis der Server antwortet "Ich bin inaktiv".

Heutzutage haben moderne lokale Fahrer ziemlich kreative Ideen, was es bedeutet, untätig zu sein. Ist es "alle Pixel sind gezeichnet" oder "meine Befehlswarteschlange hat Platz"? Auch weil viele alte Programme ohne Grund glFlush und glFinish durch ihren Code streuten, werden sie von vielen modernen Treibern einfach als "Optimierung" ignoriert. Ich kann ihnen nicht wirklich die Schuld geben.

Fazit: Behandeln Sie sowohl glFinish als auch glFlush in der Praxis als "no ops", es sei denn, Sie codieren für einen alten Remote-SGI-OpenGL-Server.

5
starmole

Schauen Sie mal hier . Kurz gesagt heißt es:

glFinish () hat den gleichen Effekt wie glFlush (), mit dem Zusatz, dass glFinish () blockiert, bis alle übergebenen Befehle ausgeführt wurden.

Ein anderes Artikel beschreibt andere Unterschiede:

  • Swap-Funktionen (in doppelt gepufferten Anwendungen verwendet) leeren die Befehle automatisch, sodass kein Aufruf von glFlush erforderlich ist.
  • glFinish zwingt OpenGL, ausstehende Befehle auszuführen, was eine schlechte Idee ist (z. B. mit VSync)

Zusammenfassend bedeutet dies, dass Sie diese Funktionen nicht einmal benötigen, wenn Sie die doppelte Pufferung verwenden, es sei denn, Ihre Swap-Buffer-Implementierung löscht die Befehle nicht automatisch.

4
AndiDog

Es scheint keine Möglichkeit zu geben, den Status des Puffers abzufragen. Es gibt dieses Apple-Erweiterung , das denselben Zweck erfüllen könnte, aber nicht plattformübergreifend zu sein scheint (ich habe es nicht ausprobiert). Auf den ersten Blick scheint es vor flush Sie würden den Zaunbefehl einschieben; Sie können dann den Status dieses Zauns abfragen, während er sich durch den Puffer bewegt.

Ich frage mich, ob Sie vor dem Puffern von Befehlen flush verwenden könnten, bevor Sie beginnen, das nächste Bild zu rendern, das Sie finish aufrufen. Auf diese Weise können Sie mit der Verarbeitung des nächsten Frames beginnen, während die GPU funktioniert. Wenn dies jedoch bis zu Ihrer Rückkehr nicht der Fall ist, wird finish blockiert, um sicherzustellen, dass sich alles in einem neuen Zustand befindet.

Ich habe es nicht ausprobiert, werde es aber in Kürze tun.

Ich habe es mit einer alten Anwendung versucht, die sogar eine CPU- und GPU-Auslastung aufweist. (Ursprünglich wurde finish verwendet.)

Als ich es am Ende in flush und am Anfang in finish änderte, gab es keine unmittelbaren Probleme. (Alles sah gut aus!) Die Reaktionsfähigkeit des Programms nahm zu, wahrscheinlich weil die CPU nicht blockiert war und auf die GPU wartete. Auf jeden Fall eine bessere Methode.

Zum Vergleich habe ich finished am Anfang des Frames entfernt und flush belassen, und es wurde dasselbe ausgeführt.

Ich würde also sagen, verwende flush und finish, denn wenn der Puffer beim Aufruf von finish leer ist, gibt es keinen Leistungseinbruch. Und ich schätze, wenn der Puffer voll wäre, sollten Sie trotzdem finish wollen.

3
GManNickG

Die Frage ist: Soll Ihr Code weiterhin ausgeführt werden, während die OpenGL-Befehle ausgeführt werden, oder soll er nur ausgeführt werden nachdem Ihre OpenGL-Befehle ausgeführt wurden?.

Dies kann in Fällen, wie bei Netzwerkverzögerungen, von Bedeutung sein, in denen nur bestimmte Konsolenausgaben ausgegeben werden nachdem die Bilder gezeichnet wurden oder dergleichen.

0
anon