Wie funktioniert OpenGL auf der untersten Ebene? [geschlossen]

82

Ich verstehe, wie man OpenGL / DirectX-Programme schreibt, und ich kenne die Mathematik und die konzeptionellen Dinge dahinter, aber ich bin gespannt, wie die GPU-CPU-Kommunikation auf einer niedrigen Ebene funktioniert.

Angenommen, ich habe ein OpenGL-Programm in C geschrieben, das ein Dreieck anzeigt und die Kamera um 45 Grad dreht. Wenn ich dieses Programm kompiliere, wird es in eine Reihe von ioctl-Aufrufen umgewandelt, und der GPU-Treiber sendet dann die entsprechenden Befehle an die GPU, wo die gesamte Logik des Drehens des Dreiecks und des Einstellens der entsprechenden Pixel in der entsprechenden Farbe verdrahtet ist im? Oder wird das Programm zu einem "GPU-Programm" kompiliert, das auf die GPU geladen wird und die Rotation usw. berechnet? Oder etwas ganz anderes?

Bearbeiten : Ein paar Tage später fand ich diese Artikelserie, die im Grunde die Frage beantwortet: http://fgiesen.wordpress.com/2011/07/01/a-trip-through-the-graphics-pipeline-2011-part- 1 /

Benno
quelle
Die OpenGL-Spezifikation gibt an, dass Befehle in einem Client / Server-Modus gesendet werden. Die Spezifikation ist zu weit gefasst, um eine bestimmte Technologie zu bestimmen, die von OpenGL-Implementierungen verwendet wird.
Luca

Antworten:

86

Diese Frage ist fast unmöglich zu beantworten, da OpenGL für sich genommen nur eine Front-End-API ist. Solange eine Implementierung der Spezifikation entspricht und das Ergebnis dieser entspricht, kann dies nach Ihren Wünschen erfolgen.

Die Frage könnte gewesen sein: Wie funktioniert ein OpenGL-Treiber auf der untersten Ebene? Dies ist nun wieder generell nicht mehr zu beantworten, da ein Treiber eng mit einer Hardware verbunden ist, die möglicherweise wieder Dinge tut, wie auch immer der Entwickler sie entworfen hat.

Die Frage hätte also lauten sollen: "Wie sieht es im Durchschnitt hinter den Kulissen von OpenGL und dem Grafiksystem aus?". Schauen wir uns das von unten nach oben an:

  1. Auf der untersten Ebene gibt es ein Grafikgerät. Heutzutage sind dies GPUs, die einen Satz von Registern bereitstellen, die ihren Betrieb steuern (wobei die Register genau geräteabhängig sind), einen Programmspeicher für Shader, einen Massenspeicher für Eingabedaten (Eckpunkte, Texturen usw.) und einen E / A-Kanal für den Rest haben des Systems, über das es Daten und Befehlsströme empfängt / sendet.

  2. Der Grafiktreiber verfolgt den GPU-Status und alle Ressourcenanwendungsprogramme, die die GPU verwenden. Es ist auch für die Konvertierung oder sonstige Verarbeitung der von Anwendungen gesendeten Daten verantwortlich (Konvertieren von Texturen in das von der GPU unterstützte Pixelformat, Kompilieren von Shadern im Maschinencode der GPU). Darüber hinaus bietet es eine abstrakte, treiberabhängige Schnittstelle zu Anwendungsprogrammen.

  3. Dann gibt es die treiberabhängige OpenGL-Clientbibliothek / den Treiber. Unter Windows wird dies per Proxy über opengl32.dll geladen, auf Unix-Systemen an zwei Stellen:

    • X11 GLX-Modul und treiberabhängiger GLX-Treiber
    • und /usr/lib/libGL.so können einige treiberabhängige Elemente für das direkte Rendern enthalten

    Unter MacOS X ist dies zufällig das "OpenGL Framework".

    Dieser Teil übersetzt OpenGL-Aufrufe in Aufrufe der treiberspezifischen Funktionen in dem in (2) beschriebenen Teil des Treibers.

  4. Schließlich die eigentliche OpenGL-API-Bibliothek, opengl32.dll in Windows und unter Unix /usr/lib/libGL.so; Dadurch werden die Befehle meist nur an die eigentliche OpenGL-Implementierung weitergegeben.

Wie die eigentliche Kommunikation abläuft, kann nicht verallgemeinert werden:

Unter Unix kann die 3 <-> 4-Verbindung entweder über Sockets (ja, möglicherweise über das Netzwerk, wenn Sie möchten) oder über Shared Memory erfolgen. In Windows werden sowohl die Schnittstellenbibliothek als auch der Treiberclient in den Prozessadressraum geladen. Das ist also nicht so viel Kommunikation, sondern einfache Funktionsaufrufe und Variablen- / Zeigerübergabe. In MacOS X ähnelt dies Windows, nur dass es keine Trennung zwischen OpenGL-Schnittstelle und Treiberclient gibt (dies ist der Grund, warum MacOS X so langsam mit neuen OpenGL-Versionen Schritt hält, dass immer ein vollständiges Betriebssystem-Upgrade erforderlich ist, um das neue zu liefern Rahmen).

Die Kommunikation zwischen 3 <-> 2 kann über ioctl, Lesen / Schreiben oder durch Zuordnen eines Speichers zum Prozessadressraum und Konfigurieren der MMU erfolgen, um Treibercode auszulösen, wenn Änderungen an diesem Speicher vorgenommen werden. Dies ist auf jedem Betriebssystem ziemlich ähnlich, da Sie immer die Kernel / Userland-Grenze überschreiten müssen: Letztendlich durchlaufen Sie einen Systemaufruf.

Die Kommunikation zwischen System und GPU erfolgt über den Periphialbus und die von ihm definierten Zugriffsmethoden, also PCI, AGP, PCI-E usw., die über Port-E / A, speicherabgebildete E / A, DMA, IRQs funktionieren.

Datenwolf
quelle
1
Diese Beschreibung ist wahrscheinlich zu niedrig und allgemein mit sehr wenigen OpenGL-Besonderheiten. Dies gilt ungefähr für alle Hardwarekomponenten, die in einem Computer vorhanden sind.
v.shashenko
1
@ v.shashenko: Na klar. Unabhängig davon, mit welcher Hardwarekomponente Sie sprechen, folgt sie diesem allgemeinen Schema. OpenGL ist jedoch keine bestimmte Software, sondern nur eine Spezifikation, und alles , was der Spezifikation entspricht, ist eine gültige Implementierung. Und da eine OpenGL-Implementierung am Ende mit einer GPU spricht, folgt sie ungefähr demselben Skript, in dem Hardware-APIs implementiert sind. Es ist unmöglich, genauer zu werden, da es keine OpenGL-Implementierung " The ONE " gibt. Und jede Implementierung ist ein bisschen anders.
Datenwolf
1
@ v.shashenko: Wenn Sie weitere Details wünschen, müssen Sie nach einer bestimmten Implementierung fragen. Beispiel: X11 + GLX + DRI → Mesa / AMDGPU → Linux-KMS + DRM (Direct Rendering Manager). Und jede dieser Komponenten ist ein ziemlich komplexes Tier mit so vielen Details, dass Sie Bücher mit Details über die Funktionsweise jeder Komponente füllen können. Das würde aber die Linux-AMDGPU-Implementierung beschreiben. Aber Linux + NVidia ist ein ganz anderes Biest. Und unter Windows ist es wieder anders.
Datenwolf
Erinnerst du dich an mich, dass wir uns vor ein paar Tagen unterhalten haben? Ich wollte nur wissen, wie ich dich kontaktieren kann, falls ich etwas wissen möchte.
Suraj Jain
Was Sie an diesem Tag gelehrt haben, hat viele meiner Zweifel ausgeräumt und auch einige neue gemacht, aber insgesamt halte ich es jetzt nicht für eine Magie, dass wir Fenster und Symbole auf den Bildschirm zeichnen.
Suraj Jain
23

Wenn ich dieses Programm kompiliere, wird es in eine Reihe von ioctl-Aufrufen umgewandelt, und der GPU-Treiber sendet dann die entsprechenden Befehle an die GPU, wo die gesamte Logik des Drehens des Dreiecks und des Einstellens der entsprechenden Pixel in der entsprechenden Farbe verdrahtet ist im? Oder wird das Programm zu einem "GPU-Programm" kompiliert, das auf die GPU geladen wird und die Rotation usw. berechnet?

Du bist nicht weit weg. Ihr Programm ruft den installierbaren Client-Treiber auf (der eigentlich kein Treiber ist, sondern eine gemeinsam genutzte Userspace-Bibliothek). Dabei wird ioctl oder ein ähnlicher Mechanismus verwendet, um Daten an den Kerneltreiber zu übergeben.

Für den nächsten Teil kommt es auf die Hardware an. Ältere Grafikkarten hatten eine sogenannte "Pipeline mit festen Funktionen". Auf der Grafikkarte befanden sich dedizierte Speicherplätze für Matrizen und dedizierte Hardware für die Textur-Suche, das Mischen usw. Der Grafiktreiber lud die richtigen Daten und Flags für jede dieser Einheiten und richtete dann DMA ein, um Ihre Scheitelpunktdaten (Position) zu übertragen , Farbe, Texturkoordinaten usw.).

Neuere Hardware verfügt über Prozessorkerne ("Shader") in der Grafikkarte, die sich von Ihrer CPU dadurch unterscheiden, dass sie jeweils viel langsamer laufen, aber viel mehr davon parallel arbeiten. Für diese Grafikkarten bereitet der Treiber Programm-Binärdateien für die Ausführung auf den GPU-Shadern vor.

Ben Voigt
quelle
19

Ihr Programm ist nicht für eine bestimmte GPU kompiliert. Es ist nur dynamisch mit einer Bibliothek verknüpft, die OpenGL implementiert. Die eigentliche Implementierung kann das Senden von OpenGL-Befehlen an die GPU, das Ausführen von Software-Fallbacks, das Kompilieren von Shadern und deren Senden an die GPU oder sogar das Verwenden von Shader-Fallbacks an OpenGL-Befehle umfassen. Die Grafiklandschaft ist ziemlich kompliziert. Dank der Verknüpfung werden Sie von der Komplexität der Treiber weitgehend isoliert, sodass die Treiberimplementierer die von ihnen für geeignet erachteten Techniken verwenden können.

Tobu
quelle
17

C / C ++ - Compiler / Linker machen genau eines: Sie konvertieren Textdateien in eine Reihe von maschinenspezifischen Opcodes, die auf der CPU ausgeführt werden. OpenGL und Direct3D sind nur C / C ++ - APIs. Sie können Ihren C / C ++ - Compiler / Linker nicht auf magische Weise in einen Compiler / Linker für die GPU konvertieren.

Jede Zeile C / C ++ - Code, die Sie schreiben, wird auf der CPU ausgeführt. Aufrufe von OpenGL / Direct3D rufen statische oder dynamische C / C ++ - Bibliotheken auf.

Der einzige Ort, an dem ein "GPU-Programm" ins Spiel kommen würde, ist, wenn Ihr Code explizit Shader erstellt. Das heißt, wenn Sie die API-Aufrufe in OpenGL / D3D ausführen, die das Kompilieren und Verknüpfen von Shadern verursachen. Zu diesem Zweck generieren oder laden Sie (zur Laufzeit, nicht zur C / C ++ - Kompilierungszeit) Zeichenfolgen, die Shader in einer Shader-Sprache darstellen. Sie schieben sie dann durch den Shader-Compiler und erhalten ein Objekt in dieser API zurück, das diesen Shader darstellt. Anschließend wenden Sie einen oder mehrere Shader auf einen bestimmten Rendering-Befehl an. Jeder dieser Schritte erfolgt explizit in Richtung Ihres C / C ++ - Codes, der wie zuvor angegeben auf der CPU ausgeführt wird.

Viele Shader-Sprachen verwenden C / C ++ - ähnliche Syntax. Das macht sie aber nicht gleichbedeutend mit C / C ++.

Nicol Bolas
quelle
Ich habe in letzter Zeit OpenGL gelernt und mich gefragt, warum ich all diese "Shader-Programme" in Code sehe, den ich als Wurfzeichenfolgen betrachte. Wie Sie sagten, werden sie zur Laufzeit kompiliert. Ich habe einige Beispiele in einem Android-Code gesehen. Dann habe ich auch einen Code für das iPhone gesehen. Anscheinend verfügt das iPhone über vier Vektorprozessoren, die Gleitkomma-Berechnungen viel schneller durchführen können als die CPU des iPhones. Sie können den ASM also als Literalzeichenfolgen schreiben, um ihn zur Laufzeit zu kompilieren und diese Prozessoren anstelle der Haupt-CPU zu verwenden.
Eggie5
1
Der größte Teil der "Pipeline mit festen Funktionen" wird jetzt von einem Standardsatz von Shader-Programmen implementiert. Sie müssen keinen Shader explizit anfordern, um einen zu erhalten.
Ben Voigt
1
@ Ben Voigt: Stimmt, aber wenn dir niemand davon erzählt hätte, könntest du den Unterschied nicht erkennen.
Nicol Bolas
1
Was eine vernünftige Haltung sein könnte, wenn diese Frage nicht die verborgenen Details betrifft.
Ben Voigt