opengl: glFlush () vs. glFinish ()

105

Ich habe Probleme, den praktischen Unterschied zwischen Anrufen glFlush()und zu unterscheiden glFinish().

Die Dokumente sagen dies glFlush()und glFinish()werden alle gepufferten Operationen an OpenGL senden, so dass man sicher sein kann, dass sie alle ausgeführt werden. Der Unterschied besteht darin, dass sie glFlush()sofort als glFinish()Blöcke zurückgegeben werden, bis alle Operationen abgeschlossen sind.

Nachdem ich die Definitionen gelesen hatte, stellte ich fest, dass ich bei Verwendung glFlush()wahrscheinlich auf das Problem stoßen würde, mehr Operationen an OpenGL zu senden, als ausgeführt werden könnten. Also, nur um es zu versuchen, tauschte ich meine glFinish()gegen a glFlush()und siehe da, mein Programm lief (soweit ich das beurteilen konnte), genau das gleiche; Bildraten, Ressourcennutzung, alles war gleich.

Ich frage mich also, ob es einen großen Unterschied zwischen den beiden Aufrufen gibt oder ob mein Code sie nicht anders laufen lässt. Oder wo einer gegen den anderen verwendet werden sollte. Ich dachte mir auch, dass OpenGL einen Aufruf haben würde, um glIsDone()zu überprüfen, ob alle gepufferten Befehle für a glFlush()vollständig sind oder nicht (so sendet man Operationen nicht schneller an OpenGL, als sie ausgeführt werden können), aber ich konnte keine solche Funktion finden .

Mein Code ist die typische Spielschleife:

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

Antworten:

93

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 Frontpuffer zeichnen, wird sichergestellt, dass die OpenGL-Treiber ohne zu große Verzögerung 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 Auswirkungen, 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 das letzte Pixel gezeichnet ist und OpenGL nichts mehr zu tun hat. Wenn Sie direkt in den Frontpuffer rendern, müssen Sie glFinish ausführen, bevor Sie die Betriebssystemaufrufe zum Erstellen von Screenshots verwenden. Für die doppelte Pufferung ist dies weitaus weniger nützlich, da die Änderungen, die Sie abschließen müssen, nicht angezeigt werden.

Wenn Sie also doppelte Pufferung verwenden, benötigen Sie wahrscheinlich weder glFlush noch glFinish. SwapBuffers leitet die OpenGL-Aufrufe implizit an den richtigen Puffer weiter. Es ist nicht erforderlich, zuerst glFlush aufzurufen . Und es macht nichts aus, den OpenGL-Treiber zu betonen: glFlush erstickt nicht an zu vielen Befehlen. Es kann nicht garantiert werden, dass dieser Aufruf sofort zurückgegeben wird (was auch immer das bedeutet), daher kann es jederzeit dauern, bis Ihre Befehle verarbeitet sind.

Malte Clasen
quelle
1
Was meinst du damit, dass glFlush "in ENDLICHER Zeit abgeschlossen sein muss" und später sagt, dass glFlush nicht erstickt, weil "es JEDE Zeit dauern kann, die es benötigt, um deine Befehle zu verarbeiten"? (Kappen von mir) Schließen sich diese nicht gegenseitig aus?
Praxiteles
1
Solange es irgendwann fertig ist, erfüllt es die FINITE-Zeitkriterien. Einige Fahrer haben diesen Anruf nicht vollständig ausgeführt.
Chrisvarnz
SwapBuffers ist kontextspezifisch und muss nur den aufrufenden Thread-Kontext leeren. Wenn mehrere Kontexte gerendert werden, sollte jeder manuell geleert werden, da dies nicht garantiert ist, wenn Puffer durch einen anderen Kontext ausgetauscht werden.
Andreas
22

Wie die anderen Antworten angedeutet haben, 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 zu erledigen hat - die Befehle wurden auf die Grafikhardware übertragen. Die allgemeine Absicht von glFinish()ist, dass nach der Rückkehr keine verbleibende Arbeit mehr ü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 usw.). Ob das wirklich passiert, hängt vom Fahrer ab. Die Spezifikation erlaubt eine Menge Spielraum, was legal ist.

Andy Ross
quelle
18

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 nicht an die Hardware, es sei denn, eine bestimmte Anzahl von Befehlen wurde gesammelt. 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 dies glFlush()auf die Befehle in der Warteschlange auswirkt. Der Treiber wird angewiesen, alle Befehle in der Warteschlange an die Hardware zu senden (auch wenn die Warteschlange noch nicht voll ist). Dies blockiert den aufrufenden Thread nicht. Es signalisiert dem Treiber 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 warten muss, bis alle Befehle von der Hardware verarbeitet wurden.

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

Tara
quelle
Ich weiß tatsächlich was glFlushund glFinishtue, und ich kann nicht sagen, was dieses Bild sagt. Was ist auf der linken und der rechten Seite? Wurde das Bild auch gemeinfrei oder unter einer Lizenz veröffentlicht, mit der Sie es im Internet veröffentlichen können?
Nicol Bolas
Ich hätte etwas näher darauf eingehen sollen. Über die Lizenz: Ich bin mir eigentlich nicht ganz sicher. Ich bin bei meinen Recherchen auf das PDF bei Google gestoßen.
Tara
7

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

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, zwingen Sie den Treiber im Wesentlichen, die Befehle an die GPU zu senden (die bis dahin gestapelt wurde und die GPU nie zur Bearbeitung aufgefordert hat) und die CPU anzuhalten, bis die übertragenen Befehle vollständig sind hingerichtet. Während der gesamten Zeit, in der die GPU arbeitet, funktioniert die CPU nicht (zumindest in diesem Thread). Und während die CPU ihre Arbeit erledigt (meistens Batching-Befehle), macht die GPU nichts. Also ja, glFinish sollte Ihre Leistung beeinträchtigen. (Dies ist eine Annäherung, da Treiber möglicherweise die GPU bei einigen Befehlen arbeiten lassen, wenn viele von ihnen bereits gestapelt wurden. Dies ist jedoch nicht typisch, da die Befehlspuffer in der Regel groß genug sind, um viele Befehle aufzunehmen.)

Warum sollten Sie dann überhaupt glFinish nennen? Ich habe es nur benutzt, wenn ich Treiberfehler hatte. Wenn einer der Befehle, die Sie an die Hardware senden, die GPU zum Absturz bringt, können Sie am einfachsten nach jedem Draw glFinish aufrufen, um festzustellen, welcher Befehl der Schuldige ist. Auf diese Weise können Sie eingrenzen, was genau den Absturz auslöst

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

Bahbar
quelle
5
Auf "Nun, warum sollten Sie dann überhaupt glFinish nennen?". Wenn Sie Ressourcen (z. B. Texturen) in einen Thread laden und diese zum Rendern in einem anderen Thread verwenden, indem Sie sie über wglShareLists oder Ähnliches freigeben, müssen Sie glFinish aufrufen, bevor Sie dem Rendering-Thread signalisieren, dass es kostenlos ist, das asynchron geladene zu rendern.
Marco Mp
Wie ich unten sagte, sieht dies wie eine Problemumgehung für Treiberfehler aus. Ich kann keine spezielle Erwähnung dieser Anforderung finden. Unter Windows muss der Treiber gesperrt sein, unter Mac muss CGLLockContext ausgeführt werden. Die Verwendung von glFinish scheint jedoch sehr falsch zu sein. Um zu wissen, wann eine Textur gültig ist, müssen Sie auf jeden Fall Ihre eigene Sperre oder Ihr eigenes Ereignis durchführen.
Starmole
1
opencl opengl interop docs empfehlen glFinish für die Synchronisation "Vor dem Aufruf von clEnqueueAcquireGLObjects muss die Anwendung sicherstellen, dass alle ausstehenden GL-Vorgänge, die auf die in mem_objects angegebenen Objekte zugreifen, abgeschlossen wurden. Dies kann portabel ausgeführt werden, indem für alle ein glFinish-Befehl ausgegeben und auf dessen Abschluss gewartet wird GL-Kontexte mit ausstehenden Verweisen auf diese Objekte "
Mark Essel
1
Sie müssen glFinish nicht "aufrufen", sondern können Synchronisierungsobjekte verwenden.
Chrisvarnz
Nur um hinzuzufügen, seit der ursprünglichen Antwort: Einige GL-Implementierungen verwendeten Flush / Finish, um zwischen gemeinsam genutzten Kontexten in verschiedenen Threads zu synchronisieren: developer.apple.com/library/ios/documentation/3DDrawing/… - Vor allem Apple IOS. Ich denke, es ist grundsätzlich ein weiterer Missbrauch der API. Dies ist jedoch eine Einschränkung meiner ursprünglichen Antwort: Bei einigen Implementierungen ist Finish / Flush erforderlich. Das Wie und Warum ist jedoch definiert, nicht die OpenGL-Spezifikation.
Starmole
7

glFlush geht wirklich auf ein Client-Server-Modell zurück. Sie senden alle gl-Befehle über eine Pipe an einen gl-Server. Diese Pipe könnte puffern. Genau wie jede Datei oder Netzwerk-E / A puffern könnte. glFlush sagt nur "Sende den Puffer jetzt, auch wenn er noch nicht voll ist!". Auf einem lokalen System wird dies fast nie benötigt, da sich eine lokale OpenGL-API wahrscheinlich nicht selbst puffert und nur Befehle direkt ausgibt. Außerdem führen alle Befehle, die das tatsächliche Rendern verursachen, eine implizite Leerung durch.

glFinish hingegen wurde zur Leistungsmessung entwickelt. Eine Art PING an den GL-Server. Es löst einen Befehl aus und wartet, bis der Server mit "Ich bin inaktiv" antwortet.

Heutzutage haben moderne, lokale Fahrer ziemlich kreative Ideen, was es bedeutet, untätig zu sein. Ist es "alle Pixel werden gezeichnet" oder "meine Befehlswarteschlange hat Platz"? Auch weil viele alte Programme glFlush und glFinish ohne Grund als Voodoo-Codierung in ihren Code gestreut haben, ignorieren viele moderne Treiber sie einfach als "Optimierung". Ich kann sie nicht wirklich dafür verantwortlich machen.

Zusammenfassend: Behandeln Sie sowohl glFinish als auch glFlush in der Praxis als keine Operationen, es sei denn, Sie codieren für einen alten Remote-SGI-OpenGL-Server.

Starmole
quelle
4
Falsch. Wie in dem Kommentar, den ich in der obigen Antwort hinterlassen habe: Wenn Sie Ressourcen (z. B. Texturen) in einen Thread laden und sie zum Rendern in einem anderen verwenden, indem Sie sie über wglShareLists oder Ähnliches teilen, müssen Sie glFinish aufrufen, bevor Sie dem Rendering-Thread eine Signalisierung senden dass es kostenlos ist, das zu rendern, was asynchron geladen wurde. Und es ist kein No-Op, wie du gesagt hast.
Marco Mp
"Ja wirklich?" Können Sie die Notwendigkeit unterstützen, glFinish aufzurufen, bevor Sie ein freigegebenes Objekt mit einem Spezifikationslink verwenden? Ich kann einen Buggy-Fahrer sehen, der das braucht. Und diese Art kommt zurück zu dem, was ich gesagt habe. Die Leute benutzen glFinish, um fehlerhafte Fahrer zu umgehen. Aus diesem Grund ignorieren Treiber, die ordnungsgemäß funktionieren, die Leistung. Der ursprüngliche Anwendungsfall der Profilerstellung (und des einfach gepufferten Renderns) funktioniert nicht mehr.
Starmole
2
Persönliche Erfahrung im Umgang mit beschädigten Texturen und dazu: Higherorderfun.com/blog/2011/05/26/… Wenn Sie darüber nachdenken, ist dies jedoch sinnvoll, da es sich nicht wesentlich von Mutexen oder anderen Synchronisierungen unterscheidet. API zwischen Threads - Bevor ein Kontext eine gemeinsam genutzte Ressource verwenden kann, muss der andere das Laden dieser Ressource abschließen, sodass sichergestellt werden muss, dass der Puffer geleert wurde. Ich denke eher an den Eckfall der Spezifikationen als an den Fehler, aber ich habe keinen Beweis für diese Aussage :)
Marco Mp
Super Antwort, danke. Besonders "viele alte Programme haben glFlush und glFinish ohne Grund als Voodoo in ihren Code gestreut".
Akauppi
Ist es wirklich kein Einsatz? Sind glFinish und / oder glFlush nicht sehr wertvoll für die Bildverarbeitung mit hoher Intensität oder die GPU-basierte mathematische Coprozessierung? Beispielsweise lesen wir die Ergebnisse der GPU-Berechnung erst nach dem Aufruf von glFinish aus dem Pixelpuffer in die CPU , um sicherzustellen, dass alle Pixelberechnungen geschrieben wurden. Vermissen wir etwas?
Praxiteles
5

Schauen Sie hier . Kurz gesagt heißt es:

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

Ein anderer Artikel beschreibt andere Unterschiede:

  • Swap-Funktionen (in doppelt gepufferten Anwendungen verwendet) leeren die Befehle automatisch, sodass kein Aufruf erforderlich ist glFlush
  • 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-Puffer-Implementierung löscht die Befehle nicht automatisch.

AndiDog
quelle
Ja, richtig, aber das Dokument besagt, dass beide zum Beispiel mit nur wenigen Unterschieden hinsichtlich des Fenstersystems den gleichen Effekt haben . Aber trotzdem habe ich meine Antwort bearbeitet;)
AndiDog
Netter Ruf nach der Nuance. Es wird klargestellt, ob es notwendig ist, glFlush () und THEN glFinish () aufzurufen - (eine Frage, die wir hatten, nachdem wir alles oben gelesen hatten.)
Praxiteles
4

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

Ich frage mich, ob Sie flushvor dem Puffern von Befehlen, aber vor dem Rendern des nächsten von Ihnen aufgerufenen Frames verwenden könnten finish. 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 erfolgt, finishwird blockiert, um sicherzustellen, dass sich alles in einem neuen Zustand befindet.

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

Ich habe es mit einer alten Anwendung versucht, die eine ziemlich gleichmäßige CPU- und GPU-Nutzung hat. (Es wurde ursprünglich verwendet finish.)

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

Zum Vergleich entfernte ich mich finishedvom Anfang des Rahmens und gingflush , und es hat dasselbe ausgeführt.

Ich würde also use flushund sagen finish, denn wenn der Puffer beim Aufruf von leer ist finish, gibt es keinen Leistungseinbruch. Und ich vermute, wenn der Puffer voll wäre, sollten Sie es finishtrotzdem wollen .

GManNickG
quelle
0

Die Frage ist: Möchten Sie, dass Ihr Code weiterhin ausgeführt wird, während die OpenGL-Befehle ausgeführt werden, oder erst, nachdem Ihre OpenGL-Befehle ausgeführt wurden?

Dies kann in Fällen wie bei Netzwerkverzögerungen von Bedeutung sein, wenn eine bestimmte Konsolenausgabe erst nach dem Zeichnen der Bilder oder dergleichen erfolgt.

anon
quelle