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 /
Antworten:
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:
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.
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.
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:
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.
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.
quelle
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.
quelle
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.
quelle
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 ++.
quelle