Wie interagieren CPU und GPU bei der Anzeige von Computergrafiken?

32194
JohnnyFromBF

Hier sehen Sie einen Screenshot eines kleinen C ++ - Programms namens Triangle.exe mit einem Dreieck, das auf der OpenGL-API basiert.

enter image description here

Zugegebenermaßen ein sehr einfaches Beispiel, aber ich denke, es ist auf andere Grafikkartenvorgänge anwendbar.

Ich war nur neugierig und wollte den gesamten Prozess durch Doppelklicken auf Triangle.exe unter Windows XP wissen, bis ich das Dreieck auf dem Monitor drehen kann. Was passiert, wie interagieren CPU (die zuerst mit der EXE-Datei arbeitet) und GPU (die schließlich das Dreieck auf dem Bildschirm ausgibt)?

Ich denke, bei der Darstellung dieses Dreiecks handelt es sich unter anderem um folgende Hardware / Software:

Hardware

  • HDD
  • Systemspeicher (RAM)
  • Zentralprozessor
  • Videospeicher
  • GPU
  • LCD Bildschirm

Software

  • Betriebssystem
  • DirectX / OpenGL-API
  • Nvidia-Fahrer

Kann jemand den Prozess erklären, vielleicht mit einem Flussdiagramm zur Illustration?

Es sollte sich nicht um eine komplexe Erklärung handeln, die jeden einzelnen Schritt abdeckt (eine Vermutung, die den Rahmen sprengen würde), sondern eine Erklärung, die ein IT-Intermediär folgen kann.

Ich bin mir ziemlich sicher, dass viele Leute, die sich selbst als IT-Experten bezeichnen würden, diesen Prozess nicht richtig beschreiben könnten.

56
Ihr Dilemma wäre vorbei, wenn Sie GPU als Erweiterung der CPU betrachten könnten! KawaiKx vor 10 Jahren 0

7 Antworten auf die Frage

52
Der Hochstapler

Ich beschloss, etwas über den Programmieraspekt zu schreiben und wie Komponenten miteinander kommunizieren. Vielleicht werden einige Bereiche beleuchtet.

Die Präsentation

Was braucht es, um dieses einzelne Bild, das Sie in Ihrer Frage gepostet haben, überhaupt auf dem Bildschirm zeichnen zu lassen?

Es gibt viele Möglichkeiten, ein Dreieck auf dem Bildschirm zu zeichnen. Nehmen wir zur Vereinfachung an, dass keine Scheitelpunktpuffer verwendet wurden. (Ein Vertex-Puffer ist ein Speicherbereich, in dem Sie Koordinaten speichern.) Angenommen, das Programm teilt der Grafikverarbeitungs-Pipeline einfach jeden einzelnen Vertex (ein Vertex ist nur eine Koordinate im Raum) in einer Reihe mit.

Aber bevor wir etwas zeichnen können, müssen wir zuerst ein Gerüst bauen. Wir werden später sehen, warum

// Clear The Screen And The Depth Buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);   // Reset The Current Modelview Matrix glMatrixMode(GL_MODELVIEW);  glLoadIdentity();  // Drawing Using Triangles glBegin(GL_TRIANGLES);  // Red glColor3f(1.0f,0.0f,0.0f); // Top Of Triangle (Front) glVertex3f( 0.0f, 1.0f, 0.0f);  // Green glColor3f(0.0f,1.0f,0.0f); // Left Of Triangle (Front) glVertex3f(-1.0f,-1.0f, 1.0f);  // Blue glColor3f(0.0f,0.0f,1.0f); // Right Of Triangle (Front) glVertex3f( 1.0f,-1.0f, 1.0f);  // Done Drawing glEnd(); 

Also, was hat das gemacht?

Wenn Sie ein Programm schreiben, das die Grafikkarte verwenden möchte, wählen Sie normalerweise eine Art Schnittstelle zum Treiber. Einige bekannte Schnittstellen zum Treiber sind:

  • OpenGL
  • Direct3D
  • CUDA

Für dieses Beispiel bleiben wir bei OpenGL. Jetzt haben Sie über die Schnittstelle zum Treiber alle Tools, die Sie benötigen, um Ihr Programm mit der Grafikkarte (oder dem Treiber, der dann mit der Karte spricht ) zu veranlassen.

Diese Schnittstelle bietet Ihnen bestimmte Werkzeuge . Diese Tools haben die Form einer API, die Sie von Ihrem Programm aus aufrufen können.

Diese API wird im obigen Beispiel verwendet. Lass uns genauer hinschauen.

Das Gerüst

Bevor Sie wirklich zeichnen können, müssen Sie ein Setup durchführen . Sie müssen Ihr Ansichtsfenster (den Bereich, der tatsächlich gerendert wird), Ihre Perspektive (die Kamera in Ihre Welt), das zu verwendende Anti-Aliasing definieren (um die Kanten Ihres Dreiecks zu glätten) ...

Aber wir werden uns das nicht ansehen. Wir werfen einen Blick auf die Dinge, die Sie für jeden Frame machen müssen . Mögen:

Bildschirm löschen

Die Grafik-Pipeline wird den Bildschirm nicht für jeden Frame löschen. Du musst es sagen. Warum? Deshalb:

enter image description here

Wenn Sie den Bildschirm nicht löschen, zeichnen Sie einfach jeden Frame darüber. Deshalb rufen wir glClearmit dem GL_COLOR_BUFFER_BITSet an. Das andere Bit ( GL_DEPTH_BUFFER_BIT) teilt OpenGL mit den löschen Tiefen Puffern. Dieser Puffer wird verwendet, um zu bestimmen, welche Pixel vor (oder hinter) anderen Pixeln liegen.

Transformation

enter image description here
Bildquelle

Transformation ist der Teil, in dem wir alle Eingabekoordinaten (die Eckpunkte unseres Dreiecks) nehmen und unsere ModelView-Matrix anwenden. Dies ist die Matrix, die erklärt, wie unser Modell (die Scheitelpunkte) gedreht, skaliert und verschoben (verschoben) wird.

Als nächstes wenden wir unsere Projektionsmatrix an. Dadurch werden alle Koordinaten so verschoben, dass sie unserer Kamera korrekt gegenüberliegen.

Jetzt verwandeln wir uns noch einmal mit unserer Viewport-Matrix. Wir tun dies, um unser Modell an die Größe unseres Monitors anzupassen. Jetzt haben wir eine Reihe von Scheitelpunkten, die gerendert werden können!

Wir werden später etwas zurückkehren.

Zeichnung

Um ein Dreieck zu zeichnen, können Sie OpenGL einfach anweisen, eine neue Liste von Dreiecken zu starten, indem Sie glBeginmit der GL_TRIANGLESKonstante aufrufen .
Es gibt auch andere Formen, die Sie zeichnen können. Wie ein Dreieckstreifen oder ein Dreieckfächer . Dies sind in erster Linie Optimierungen, da sie weniger Kommunikation zwischen der CPU und der GPU erfordern, um die gleiche Anzahl von Dreiecken zu zeichnen.

Danach können wir eine Liste von drei Scheitelpunkten bereitstellen, aus denen jedes Dreieck bestehen soll. Jedes Dreieck verwendet 3 Koordinaten (da wir uns im 3D-Raum befinden). Zusätzlich kann ich für jeden Scheitelpunkt eine Farbe angeben, indem ich glColor3f vor dem Aufruf aufrufe glVertex3f.

Die Schattierung zwischen den 3 Eckpunkten (den drei Ecken des Dreiecks) wird von OpenGL automatisch berechnet . Die Farbe wird über die gesamte Fläche des Polygons interpoliert.

Interaktion

Wenn Sie jetzt auf das Fenster klicken. Die Anwendung muss nur die Fenstermeldung erfassen, die den Klick signalisiert. Dann können Sie eine beliebige Aktion in Ihrem Programm ausführen.

Dies wird viel schwieriger, wenn Sie mit Ihrer 3D-Szene interagieren möchten.

Sie müssen zunächst klar wissen, bei welchem ​​Pixel der Benutzer auf das Fenster geklickt hat. Unter Berücksichtigung Ihrer Perspektive können Sie dann die Richtung eines Strahls vom Mausklick in Ihre Szene berechnen. Sie können dann berechnen, ob sich ein Objekt in Ihrer Szene mit diesem Strahl schneidet . Jetzt wissen Sie, ob der Benutzer auf ein Objekt geklickt hat.

Also, wie lässt du es drehen?

Transformation

Mir sind zwei Arten von Transformationen bekannt, die im Allgemeinen angewendet werden:

  • Matrixbasierte Transformation
  • Knochenbasierte Transformation

Der Unterschied ist, dass Knochen einzelne Scheitelpunkte beeinflussen . Matrizen wirken sich immer auf alle gezeichneten Scheitelpunkte auf dieselbe Weise aus. Schauen wir uns ein Beispiel an.

Beispiel

Zuvor haben wir unsere Identitätsmatrix geladen , bevor Sie unser Dreieck gezeichnet haben. Die Identitätsmatrix ist eine, die einfach keine Transformation liefert . Was immer ich zeichne, wird nur von meiner Perspektive beeinflusst. Das Dreieck wird also überhaupt nicht gedreht.

Wenn ich es jetzt drehen möchte, könnte ich entweder selbst rechnen (auf der CPU) und einfach glVertex3fmit anderen Koordinaten (die gedreht werden) aufrufen . Oder ich könnte die GPU die ganze Arbeit machen lassen, indem ich glRotatefvor dem Zeichnen anrufe:

// Rotate The Triangle On The Y axis glRotatef(amount,0.0f,1.0f,0.0f);  

amountist natürlich nur ein fester Wert. Wenn Sie animieren möchten, müssen Sie amountihn bei jedem Frame nachverfolgen und vergrößern.

Also, warten Sie, was ist mit all den Matrix-Gesprächen früher passiert?

In diesem einfachen Beispiel müssen wir uns nicht um Matrizen kümmern. Wir rufen einfach an glRotatefund kümmern uns um all das.

glRotateerzeugt eine Drehung angleum den Vektor xyz. Die aktuelle Matrix (siehe glMatrixMode ) wird mit einer Rotationsmatrix multipliziert, wobei das Produkt die aktuelle Matrix ersetzt, als ob glMultMatrix mit der folgenden Matrix als Argument aufgerufen würde:

x 2 - 1 - c + cx - y - 1 - c - z - sx - z - 1 - c + y - 0 - y - x - 1 - c + z - sy 2 - 1 - c + cy - z 1 - c - x ⁢ s 0 x ⁢ z ⁡ 1 - c - y ⁢ sy ⁢ z ⁡ 1 - c + x ⁢ sz 2 ⁡ 1 - c + c 0 0 0 0 1

Vielen Dank dafür!

Fazit

Es wird offensichtlich, dass mit OpenGL viel geredet wird . Aber es sagt uns nichts. Wo ist die Kommunikation?

Das einzige, was OpenGL uns in diesem Beispiel sagt, ist, wann es fertig ist . Jede Operation dauert eine gewisse Zeit. Manche Operationen dauern unglaublich lange, andere sind unglaublich schnell.

Das Senden eines Scheitelpunkts an die GPU ist so schnell, dass ich gar nicht weiß, wie ich es ausdrücken soll. Das Senden tausender Scheitelpunkte von der CPU an die GPU, jedes einzelne Frame, ist höchstwahrscheinlich überhaupt kein Problem.

Das Löschen des Bildschirms kann eine Millisekunde oder weniger dauern (denken Sie daran, dass Sie normalerweise nur etwa 16 Millisekunden Zeit haben, um jedes Bild zu zeichnen), je nachdem, wie groß Ihr Ansichtsfenster ist. Um es zu löschen, muss OpenGL jedes einzelne Pixel in der Farbe zeichnen, in der Sie löschen möchten, dh Millionen Pixel.

Abgesehen davon können wir OpenGL so gut wie nur nach den Fähigkeiten unseres Grafikadapters fragen (maximale Auflösung, maximales Anti-Aliasing, maximale Farbtiefe, ...).

Wir können eine Textur aber auch mit Pixeln füllen, die jeweils eine bestimmte Farbe haben. Jedes Pixel enthält somit einen Wert und die Textur ist eine riesige "Datei", die mit Daten gefüllt ist. Wir können das in die Grafikkarte laden (durch Erstellen eines Texturpuffers), dann einen Shader laden, diesen Shader anweisen, unsere Textur als Eingabe zu verwenden, und einige extrem schwere Berechnungen an unserer "Datei" ausführen.

Wir können dann das Ergebnis unserer Berechnung (in Form neuer Farben) in eine neue Textur "umwandeln".

So können Sie die GPU auf andere Weise für Sie arbeiten lassen. Ich gehe davon aus, dass CUDA diesem Aspekt ähnlich ist, aber ich hatte nie die Gelegenheit, damit zu arbeiten.

Wir haben das ganze Thema wirklich nur wenig berührt. Die Programmierung von 3D-Grafiken ist eine Hölle.

enter image description here
Bildquelle

Gute Antwort! Hätte mehr Upvotes gehabt, aber vielleicht hilft das, im Newsletter zu sein. Linuxios vor 12 Jahren 2
36
David Schwartz

Es ist schwer zu verstehen, was genau Sie nicht verstehen.

Die GPU verfügt über eine Reihe von Registern, die das BIOS abbildet. Dadurch kann die CPU auf den Speicher der GPU zugreifen und die GPU anweisen, Vorgänge auszuführen. Die CPU steckt Werte in diese Register, um einen Teil des Speichers der GPU abzubilden, damit die CPU darauf zugreifen kann. Dann werden Anweisungen in diesen Speicher geladen. Sie schreibt dann einen Wert in ein Register, das die GPU anweist, die Anweisungen auszuführen, die die CPU in ihren Speicher geladen hat.

Die Informationen bestehen aus der Software, die die GPU ausführen muss. Diese Software wird mit dem Treiber mitgeliefert. Anschließend übernimmt der Treiber die Aufteilung der Verantwortung zwischen CPU und GPU (indem Teile des Codes auf beiden Geräten ausgeführt werden).

Der Treiber verwaltet dann eine Reihe von "Fenstern" in den GPU-Speicher, aus denen die CPU lesen und schreiben kann. Im Allgemeinen beinhaltet das Zugriffsmuster, dass die CPU Anweisungen oder Informationen in den zugeordneten GPU-Speicher schreibt und dann die GPU über ein Register anweist, diese Anweisungen auszuführen oder diese Informationen zu verarbeiten. Die Informationen umfassen Shaderlogik, Texturen usw.

Danke für Ihre Erklärung. Im Grunde habe ich nicht verstanden, wie der CPU-Befehlssatz mit dem GPU-Befehlssatz kommuniziert, aber offensichtlich ist es der Treiber, der diesen Teil übernimmt. Das meinte ich mit Abstraktionsschichten. JohnnyFromBF vor 12 Jahren 1
Es ist kein CPU-Befehlssatz beteiligt. Der Treiber und die Laufzeit kompilieren Ihre CUDA, OpenGL, Direct3D usw. in native GPU-Programme / Kernel, die dann ebenfalls in den Gerätespeicher hochgeladen werden. Der Befehlspuffer bezieht sich dann wie jede andere Ressource auf diese. Axel Gneiting vor 12 Jahren 2
Ich bin nicht sicher, auf welche Programme Sie sich beziehen (die auf der GPU laufen und in den Treibern enthalten sind). Die gpu ist größtenteils eine feste Funktionshardware, und die einzigen Programme, die sie ausführen wird, sind Shader, die von der Anwendung bereitgestellt werden, nicht vom Treiber. Der Treiber kompiliert nur diese Programme und lädt sie dann in den Speicher der gpu. Ben Richards vor 12 Jahren 2
@ sidran32: In der Kepler-Architektur von nVidia werden Kernel, Streams und Ereignisse beispielsweise von Software erstellt, die auf der GPU und nicht (normalerweise) auf der CPU ausgeführt wird. GPU-seitige Software verwaltet auch RDMA. Die gesamte Software wird vom Treiber in den GPU-Speicher geladen und läuft als "Mini-OS" auf der GPU, die die GPU-Seite des kooperierenden CPU / GPU-Paars behandelt. David Schwartz vor 12 Jahren 1
@DavidSchwartz Ich habe GPU-Rechenaufgaben vergessen. Trotzdem verhalten sie sich in der Implementierung immer noch ähnlich wie Shader. Ich würde es jedoch nicht als "Mini-OS" bezeichnen, da es nicht die gleiche Funktionalität hat, die normalerweise mit Betriebssystemen verbunden ist. Es ist immer noch eine sehr spezialisierte Software, da die GPU (aus gutem Grund) nicht wie eine CPU ausgelegt ist. Ben Richards vor 12 Jahren 0
12
Tom Wijsman

Ich war nur neugierig und wollte den gesamten Prozess durch Doppelklicken auf Triangle.exe unter Windows XP wissen, bis ich das Dreieck auf dem Monitor drehen kann. Was passiert, wie interagieren CPU (die zuerst mit der EXE-Datei arbeitet) und GPU (die schließlich das Dreieck auf dem Bildschirm ausgibt)?

Nehmen wir an, Sie wissen tatsächlich, wie eine ausführbare Datei auf einem Betriebssystem ausgeführt wird und wie diese ausführbare Datei von Ihrer GPU an den Monitor gesendet wird, wissen jedoch nicht, was dazwischen passiert. Lassen Sie uns einen Blick auf den Hardwareaspekt werfen und die Antwort des Programmierers weiter ausdehnen ...

Was ist die Schnittstelle zwischen CPU und GPU?

Mit einem Treiber kann die CPU über Motherboard- Funktionen wie PCI mit der Grafikkarte kommunizieren und Befehle an sie senden, um einige GPU-Anweisungen auszuführen, auf den GPU-Speicher zuzugreifen bzw. ihn zu aktualisieren, einen Code zu laden, der auf der GPU ausgeführt werden soll, und vieles mehr ...

Sie können jedoch nicht direkt mit der Hardware oder dem Treiber über den Code sprechen. Dies muss also durch APIs wie OpenGL, Direct3D, CUDA, HLSL, Cg. Während die ersten GPU-Anweisungen ausführen und / oder den GPU-Speicher aktualisieren, führt der letztere Code tatsächlich auf der GPU aus, da es sich um Physik / Shader-Sprachen handelt.

Warum Code auf der GPU und nicht auf der CPU ausführen?

Die CPU ist zwar gut in der Ausführung unserer täglichen Workstation- und Serverprogramme, aber an all die glänzenden Grafiken, die Sie in Spielen dieser Tage sehen, wurde nicht viel gedacht. Damals gab es Software-Renderer, die den Trick aus einigen 2D- und 3D-Dingen machten, aber sie waren sehr einschränkend. Hier kam also die GPU ins Spiel.

Die GPU ist für eine der wichtigsten Berechnungen in der Grafik, die Matrixmanipulation, optimiert . Während die CPU jede Multiplikation in einer Matrixmanipulation nacheinander berechnen muss (später Dinge wie 3DNow! Und SSE) aufgeholt), kann die GPU all diese Multiplikationen gleichzeitig ausführen! Parallelität.

Parallele Berechnungen sind jedoch nicht der einzige Grund. Ein weiterer Grund ist, dass sich die GPU viel näher am Videospeicher befindet, was sie viel schneller macht als Rundfahrten durch die CPU usw.

Wie zeigen diese GPU-Anweisungen / Speicher / Code Grafiken an?

Es fehlt ein Teil, um das alles zum Laufen zu bringen. Wir brauchen etwas, das wir schreiben können, das wir dann lesen und an den Bildschirm senden können. Wir können dies tun, indem wir einen Framebuffer erstellen . Bei jedem Vorgang werden Sie schließlich die Pixel im Framebuffer aktualisieren. die neben der Position auch Informationen über Farbe und Tiefe enthalten.

Lassen Sie uns ein Beispiel geben, in dem Sie irgendwo ein Blutsprite (ein Bild) zeichnen wollten. Zunächst wird die Baumstruktur selbst in den GPU-Speicher geladen, sodass sie auf Wunsch einfach neu gezeichnet werden kann. Um das Sprite tatsächlich irgendwo zu zeichnen, können wir das Sprite mithilfe von Scheitelpunkten übersetzen (in die richtige Position bringen), rastern (aus einem 3D-Objekt in Pixel umwandeln) und den Framebuffer aktualisieren. Um eine bessere Vorstellung zu erhalten, hier ein Flussdiagramm der OpenGL-Pipeline von Wikipedia:

Dies ist der Hauptgedanke der gesamten Grafikidee, mehr Forschung ist Hausaufgabe für den Leser.

7
AZ.

Um es einfach zu halten, können wir es so beschreiben. Einige Speicheradressen sind (vom BIOS und / oder Betriebssystem) nicht für den RAM, sondern für die Grafikkarte reserviert. Alle Daten, die mit diesen Werten geschrieben werden (Zeiger), werden auf die Karte übertragen. Theoretisch kann also jedes Programm direkt auf die Videokarte schreiben, wenn Sie nur den Adressbereich kennen. Dies ist genau so, wie es früher gemacht wurde. In der Praxis mit modernen Betriebssystemen wird dies vom Grafiktreiber und / oder der darüber liegenden Grafikbibliothek (DirectX, OpenGL usw.) verwaltet.

-1 Er fragt, wie ein DirectX-API-Aufruf von der CPU mit der GPU kommunizieren kann, und Ihre Antwort lautet "" wird vom Treiber und / oder DirectX verwaltet "*? Dies erklärt auch nicht, wie benutzerdefinierter Code (ala CUDA) ausgeführt werden kann. BlueRaja - Danny Pflughoeft vor 12 Jahren 1
bitte lernen sie zu lesen. Ich sagte, indem ich auf bestimmte Speicheradressen schreibe, die für GPU anstelle von RAM reserviert sind. Und dies erklärt, wie Sie alles ausführen können. Ein Speicherbereich wird für eine Karte registriert. Alles, was Sie in diesem Bereich schreiben, geht an die GPU, die Vertex-Verarbeitung ausführt, CUDA oder was auch immer. AZ. vor 12 Jahren 3
4
Axel Gneiting

GPUs werden normalerweise von DMA-Puffern gesteuert. Das heißt, der Treiber kompiliert die Befehle, die er vom Benutzer-Space-Programm empfängt, in einen Befehlsstrom (Status wechseln, auf diese Weise zeichnen, Kontext wechseln usw.), die dann in den Gerätespeicher kopiert werden. Sie weist dann die GPU an, diesen Befehlspuffer über ein PCI-Register oder ähnliche Verfahren auszuführen.

Bei jedem Draw-Aufruf usw. wird also der Benutzer-Space-Treiber den Befehl kompilieren, der dann den Kernel-Space-Treiber über einen Interrupt aufruft und schließlich den Befehlspuffer an den Gerätespeicher übergibt und die Wiedergabe der GPU anweist.

Auf Konsolen können Sie sogar den Spaß haben, all das selbst zu machen, insbesondere auf PS3.

0
user583641

Ich denke, die CPU sendet Videodaten über den Bus an die GPU und zeigt sie dann an. Somit kann eine schnellere GPU mehr Daten von der CPU verarbeiten. Auf diese Weise wird ein Teil der Verarbeitung von cpuoffload in GPU ausgeführt. Daher werden Sie schneller in Spielen.

Es ist wie der RAM, in dem die CPU Sachen speichert, damit sie schnell geladen und verarbeitet werden kann. Beides macht Spiele schneller.

Oder Soundkarten oder Netzkarten funktionieren nach demselben Prinzip, dh Daten abzurufen und einige CPU-Arbeit abzulegen.

Dies dupliziert eine weitere Antwort und fügt keinen neuen Inhalt hinzu. Bitte posten Sie keine Antwort, es sei denn, Sie haben tatsächlich etwas Neues beizutragen. DavidPostill vor 8 Jahren 0
0
dave

Ich denke, op ist nicht sicher, was genau die CPU der Grafikkarte sagt und warum die grafikbezogenen Befehle (wie etwa die Befehle von opengl oder direct3d) nicht direkt an die GPU gesendet werden.

Die CPU teilt der GPU nur mit, was gerendert werden soll. Alle Anweisungen durchlaufen zuerst die CPU, wo sie für die GPU für das eigentliche Rendern eingerichtet bzw. initialisiert werden.