Basierend auf diesem Artikel habe ich das Grundprinzip innerhalb des Rasterisierungsalgorithmus verstanden:
For every triangle
Compute Projection, color at vertices
Setup line equations
Compute bbox, clip bbox to screen limits
For all pixels in bbox
Increment line equations
Compute curentZ
Compute currentColor
If all line equations>0 //pixel [x,y] in triangle
If currentZ<zBuffer[x,y] //pixel is visible
Framebuffer[x,y]=currentColor
zBuffer[x,y]=currentZ
Was ich nicht verstehe ist, wie es parallel in der GPU implementiert wird.
Ich betrachte 2 mögliche Implementierungen des Algorithmus innerhalb der GPU:
Der erste Weg besteht darin, jedes Dreieck nacheinander (in einem Thread) zu zeichnen, da alle darin enthaltenen Pixel parallel ausgeführt werden. Was mich an dieser Art stört, ist, dass es für eine große Anzahl von Dreiecken sehr langsam ist.
Die zweite Möglichkeit besteht darin, jedes Dreieck parallel zu zeichnen, da für jedes Dreieck auch alle darin enthaltenen Pixel parallel ausgeführt werden. Das sieht für mich effizient aus, aber ich sehe ein Problem in der Art und Weise, wie zBuffer- und Framebuffer- Daten synchronisiert werden, als ob 2 Pixel versuchen, 1 Stelle zu belegen. Es werden 2 Threads gleichzeitig versuchen, auf dieselben Daten zu schreiben. Angesichts der Tatsache, dass es 2 Puffer gibt, die aktualisiert werden müssen, sehe ich keine Möglichkeit, wie dies atomar geschehen könnte.
Eine andere Beobachtung, die ich habe, ist, dass wenn ich 2 Dreiecke an denselben Koordinaten zeichne, es immer das letzte ist, das gezeichnet wird. Dies hindert mich daran zu denken, dass es eine atomare Möglichkeit gibt, Pixelberechnungen durchzuführen, als ob die Ausgabepixel des Dreiecks basierend auf den 2 eingegebenen Dreiecksfarben zufällig wären.
Die Sache, die ich denke, ist, dass die Implementierung etwas in der Mitte meiner 2 Vermutungen ist, aber es ist dort, wo ich aufgebe und hier frage.
quelle
Antworten:
Unterschiedliche GPUs verwenden unterschiedliche Tricks und Techniken, sodass diese Antwort absichtlich sehr allgemein gehalten wird und einige Details möglicherweise nicht für einige GPUs in der Vergangenheit, Gegenwart (2017) und Zukunft gelten.
Dies gilt hauptsächlich für Personal Computer-GPUs. GPUs für Mobiltelefone (Telefone, Tablets) versuchen, intelligenter und weniger verschwenderisch zu sein und Speicherbandbreite zu sparen, folgen jedoch einem ähnlichen Prozess. Wenn Sie alle GPUs detailliert behandeln, erhalten Sie eine sehr lange Antwort.
Die Rendering-Phasen sind absichtlich nicht in Ordnung.
Fett gedruckt sind die Teile, die die Parallelität abdecken
Die Phasen können in einer Pipeline ausgeführt werden, in der der nächste Zeichnungsaufruf verarbeitet wird, bevor der vorherige abgeschlossen ist, und während andere GPU-Kerne zeichnen.
Pixel zeichnen
Moderne GPUs zeichnen nicht mehr ein Dreieck als das nächste. Es ist viel komplizierter, die Parallelität zu maximieren:
GPUs teilen die Zeichnung in WxH-Kacheln auf (dies hängt von der GPU ab, aber für unser Beispiel gehen wir mit 8x8).
Jede Kachel kann unabhängig vom Zeichenmodus von der GPU parallel gezeichnet werden.
GPU-Kerne sind (normalerweise) in Gruppen von BxH (z. B. 8x8 = 64 Kerne) angeordnet, die der Kachel entsprechen. Eine Gruppe kann auf eine Kachel rendern, während die andere eine andere Kachel rendert .
Die gesamte Gruppe führt denselben Shader zur gleichen Zeit aus. Rendern auf alle Pixel der Kachel gleichzeitig . Wenn einige Pixel nicht gezeichnet werden sollen (dh das Dreieck bedeckt nur einen Teil der Kachel), werden diese Kerne weiterhin ausgeführt, jedoch in einem deaktivierten Modus, der das Ergebnis ignoriert und nichts schreibt. Mobile GPUs arbeiten häufig mit kleineren Kerngruppen zusammen, um weniger verschwenderisch zu sein und gleichzeitig mehrere Pixel in der Kachel zu rendern.
Auf einigen GPUs werden zwei oder mehr nicht überlappende Dreiecke , die denselben Shader verwenden, der in demselben Zeichnungsaufruf für dieselbe Kachel gerendert wurde, kombiniert und gleichzeitig in dieser Kachel gerendert .
Andere GPUs müssen für jedes Dreieck einen Durchgang durchführen.
Dies bedeutet, dass im schlimmsten Fall, wenn Sie ein Dreieck mit einer Größe von 1 Pixel haben, 1 Kern in der Gruppe etwas Nützliches zeichnet und die anderen 63 Kerne in der Kachel im Modus "Ergebnis ignorieren" "nichts" tun.
Scheitelpunkte transformieren
Dieselben Kerngruppen (einheitliche GPU) verarbeiten auch eingehende Scheitelpunkte (Scheitelpunkt-Shader).
Eingehende Scheitelpunktstapel werden jeweils N Scheitelpunkte verarbeitet (z. B. 64 oder ein Vielfaches davon in unserer obigen 8x8-Kernanordnung) und in einen transformierten temporären Puffer ausgegeben.
Die GPU kann mit der Transformation von Scheitelpunkten des nächsten Zeichnungsaufrufs beginnen, bevor die erste Transformation abgeschlossen ist, sodass die Scheitelpunktarbeit an verschiedenen Zeichnungsaufrufen parallel ausgeführt werden kann .
Clipping & Geometry Shader
Die temporären Ausgaben der Scheitelpunkttransformation werden ausgewählt und in weitere Dreiecke geschnitten. Optional wird vor dem Abschneiden ein Geometrie-Shader ausgeführt und in einen weiteren temporären Puffer geschrieben.
Jede Pufferverarbeitung aus der Scheitelpunkttransformation kann parallel ausgeführt werden.
GPU-Treiber können Vertex-, Geometrie- und / oder Clipping-Phasen intern zu einer Shader-Programm-Ausführungsstufe kombinieren.
Fliesen Dreieck Eimer
Die transformierten und abgeschnittenen Dreiecke werden in "Eimer" (Listen, Arrays, Kreispuffer oder andere Methoden) jeder Kachel gelegt, die sie abdecken. Ein Bucket für jede Renderkachel oder mehr (kann einer pro Shader in einer Warteschlange sein, abhängig von GPU und Treiber).
Wenn eine Kachel bereit ist, eine GPU-Kerngruppe zu zeichnen, "greift" sie aus der Warteschlange und beginnt mit dem Zeichnen dieser Kachel. Die nächste GPU-Kerngruppe kann die nächste Kachel greifen und so weiter ...
Es gibt im Wesentlichen zwei Möglichkeiten:
Das Obige hängt oft davon ab, ob es sich um eine Handy- (mobile) GPU oder eine Desktop- / Laptop-GPU handelt, aber nicht unbedingt.
Bei einigen GPUs wird diese Bucketing-Arbeit von einer oder mehreren Master-Koordinator-CPUs innerhalb der GPU ausgeführt, die alle "dümmeren" (aber besser zum Zeichnen von Pixeln) GPU-Kerngruppen koordinieren und ihnen mitteilen, was wann zu tun ist.
Bei anderen GPUs sind die Kerne allgemeiner und in der Lage, dies selbst oder zumindest einen Teil dieser Koordinierungsarbeit zu tun.
Auf einigen Maschinen wird diese Koordinierungsarbeit von der Hauptcomputer-CPU selbst ausgeführt.
Auf anderen (älteren oder integrierten GPU-) Computern werden die Scheitelpunkt-, Geometrie- und Beschneidungsarbeiten von der CPU selbst ausgeführt, und die GPU-Kerne erledigen nur Pixel-Shader-Arbeiten.
Die gesamte Arbeit wird in kleine Arbeitslasten und so viele Stufen wie möglich (und praktisch) unterteilt, um die Parallelität zu maximieren und ein Gleichgewicht zwischen Parallelität und Koordinierungskosten zu finden.
quelle