Ich habe eine einfache OpenGL-Test-App in C, die verschiedene Dinge als Reaktion auf Tasteneingaben zeichnet. (Mesa 8.0.4, getestet mit Mesa-EGL und mit GLFW, Ubuntu 12.04LTS auf einem PC mit NVIDIA GTX650). Die Draws sind recht einfach / schnell (rotierendes Dreieck). Mein Testcode schränkt die Framerate in keiner Weise absichtlich ein, er sieht einfach so aus:
while (true)
{
draw();
swap_buffers();
}
Ich habe dies sehr sorgfältig geplant und finde, dass die Zeit von einem eglSwapBuffers()
(oder glfwSwapBuffers
demselben) Anruf zum nächsten ~ 16,6 Millisekunden beträgt. Die Zeit von nach einem Anruf bis eglSwapBuffers()
kurz vor dem nächsten Anruf ist nur ein bisschen kürzer, obwohl das, was gezeichnet wird, sehr einfach ist. Die Zeit, die der Aufruf der Swap-Puffer benötigt, liegt deutlich unter 1 ms.
Die Zeit zwischen dem Ändern der Zeichnung der App als Reaktion auf den Tastendruck und der tatsächlich auf dem Bildschirm angezeigten Änderung beträgt jedoch> 150 ms (ca. 8-9 Frames). Dies wird mit einer Kameraaufnahme des Bildschirms und der Tastatur mit 60 fps gemessen.
Daher die Fragen:
Wo werden Draws zwischen einem Aufruf zum Austauschen von Puffern und dem tatsächlichen Anzeigen auf dem Bildschirm gepuffert? Warum die Verzögerung? Es sieht sicher so aus, als würde die App jederzeit viele Frames vor dem Bildschirm zeichnen.
Was kann eine OpenGL-Anwendung tun, um eine sofortige Auslosung auf dem Bildschirm zu bewirken? (dh: keine Pufferung, nur blockieren, bis das Zeichnen abgeschlossen ist; ich brauche keinen hohen Durchsatz, ich brauche eine niedrige Latenz)
Was kann eine Anwendung tun, um die oben genannte sofortige Auslosung so schnell wie möglich durchzuführen?
Wie kann eine Anwendung wissen, was gerade auf dem Bildschirm angezeigt wird? (Oder wie lang / wie viele Frames ist die aktuelle Pufferverzögerung?)
Antworten:
Jede von der CPU aufgerufene Zeichnungs-API-Funktion wird an den GPU- Befehlsringpuffer gesendet , um später von der GPU ausgeführt zu werden. Dies bedeutet, dass OpenGL-Funktionen meist nicht blockierende Funktionen sind. Die CPU und die GPU arbeiten also parallel.
Das Wichtigste ist, dass Ihre Anwendung CPU- oder GPU-gebunden sein kann. Sobald Sie glFinish aufrufen, sollte die CPU warten, bis die GPU ihre Zeichenbefehle ausgeführt hat. Wenn die GPU mehr Zeit benötigt und die CPU möglicherweise blockiert, sind Ihre Anwendungen an die GPU gebunden. Wenn die GPU das Zeichnen von Befehlen beendet und die CPU zu lange braucht, um fertig zu werden, ist Ihre Anwendung CPU-gebunden.
Und beachten Sie, dass es einen Unterschied zwischen
glFlush
und gibtglFinish
.glFlush
: Gibt an, dass alle Befehle, die zuvor an den GL gesendet wurden, in endlicher Zeit ausgeführt werden müssen.glFinish
: Erzwingt den Abschluss aller vorherigen GL-Befehle. Finish wird erst zurückgegeben, wenn alle Auswirkungen zuvor ausgegebener Befehle auf den GL-Client- und -Serverstatus und den Framebuffer vollständig realisiert wurden. "glXSwapBuffers führt einen impliziten glFlush durch, bevor er zurückkehrt. Nachfolgende OpenGL-Befehle können unmittelbar nach dem Aufruf von glXSwapBuffers ausgegeben werden, werden jedoch erst ausgeführt, wenn der Pufferaustausch abgeschlossen ist.
Die tatsächliche Frame-Zeit wird höchstwahrscheinlich davon abhängen, welche der beiden CPUs / GPUs mehr Zeit benötigt, um ihre Arbeit abzuschließen.
quelle
both low framerate (exactly same as monitor refresh rate??
nein, es sei denn, Sie verwenden explizit VSync.OpenGL aktualisiert den Bildschirm technisch nie.
Es gibt eine Fenstersystem-API, die von GL getrennt ist (z. B. GLX, WGL, CGL, EGL), die dies tut. Buffer Swaps, die diese APIs verwenden, werden im Allgemeinen implizit aufgerufen,
glFlush (...)
aber in einigen Implementierungen (z. B. dem GDI-Rasterizer unter Windows) wird Folgendes ausgeführtglFinish (...)
:* Auf der linken Seite befindet sich der ICD-Pfad (Hardware)
SwapBuffers (...)
. Auf der rechten Seite der GDI-Pfad (Software).Wenn Sie VSYNC aktiviert und Double-Buffering, dann jeder Befehl, der die Back - Puffer vor einer anstehenden Swap auftritt ändern würde Muss Stall bis zum Swap. Die Befehlswarteschlange hat eine begrenzte Tiefe, sodass dieser blockierte Befehl möglicherweise einen Stau in der Pipeline verursacht. Dies könnte bedeuten, dass anstelle der Blockierung
SwapBuffers (...)
Ihrer Anwendung tatsächlich ein nicht verwandter GL-Befehl blockiert wird, bis VBLANK herumrollt. Worauf es wirklich ankommt, ist, wie viele Rückpuffer Sie in Ihrer Swap-Kette haben.Solange alle hinteren Puffer voll mit fertigen Frames sind, die noch nach vorne verschoben werden müssen, verursachen Swap-Puffer implizit eine Blockierung. Leider gibt es keine Möglichkeit, die Anzahl der von den meisten GL-Fenstersystem-APIs verwendeten Backpuffer explizit zu steuern (abgesehen von 0 einfach gepufferten oder 1 doppelt gepufferten). Dem Treiber steht es frei, 2 Backpuffer zu verwenden, wenn er dies wünscht (dreifache Pufferung). Sie können dies jedoch nicht auf Anwendungsebene mit GLX oder WGL anfordern.
quelle
glFinish (...)
unmittelbar nach dem Austauschen der Puffer aufzurufen . Dies löscht die Befehlswarteschlange, bedeutet aber auch, dass die GPU und die CPU synchronisiert werden (was nicht gut ist, wenn Sie möchten, dass die GPU jederzeit funktioniert).Ich nehme an, Sie kennen dieses Experiment ?
Im Wesentlichen machte John Carmack etwas Ähnliches und zeichnete den Bildschirm und die Timing-Pixel auf, die an den Bildschirm gesendet wurden. Er stellte fest, dass ein Großteil der Latenz vom Bildschirm kam. Andere Faktoren waren die Eingangsverzögerung über die Tastatur, die Grafiktreiber und / oder natürlich die Ausführung des Programms selbst.
quelle