NV40-Technik im Detail
Teil 1: Geheimnisse der Pixelshader-Einheit
23. August 2004 / von aths / Seite 2 von 5
Wie viele Shadereinheiten hat die Pipeline?
Wir sehen die Mini-FPUs nicht als "echte" Shadereinheiten, weil sie nicht alle Instruktionen ausführen können. Inkonsequenterweise zählen wir allerdings zwei Shadereinheiten für die Pipeline eines NV40, obwohl beide Einheiten ebenfalls limitiert sind. Auf den ersten Blick scheint es sich um eine Art Allzweck-Shadercore sowie einer Zusatz-FPU zu handeln, wie wir das schon vom NV35 her kennen. Doch betrachten wir die Sache genauer:
NV40s Pipeline im Überblick.
Der Loopback, der einmal um alle diese Einheiten herum geht, ist hier nicht dargestellt. Einheit 1 besteht offensichtlich aus zwei Teilen, einer mathematischen und einer Textur-Unit.
Die mathematische Einheit 1 ist nicht in der Lage, die einfache ADD-Operation auszuführen. ADD wird von Einheit 2 geboten, welche auch ein weiteres MUL beisteuert (zwei MULs und ein ADD pro Pipe werden seit der ersten Riva TNT geboten). Was die Spezialfunktionen angeht, werden einige von Einheit 2 ausgeführt, weil Einheit 1 nicht jede Spezialfunktion berechnen kann. Beide Bausteine ergänzen sich einander zu einer perfekten Allzweck-Unit. MUL ist die einzige Operation, die beide Bausteine berechnen können. Diese beiden MULs pro Pipe sind erforderlich, um andere Instruktionen innerhalb eines Taktes auszuführen. Letztenendes haben wir eine einzelne Allzweck-Shadereinheit – jedoch mit der Fähigkeit, unter bestimmten Umständen so viel zu berechnen wie zwei herkömmliche Units. Nvidia bezeichnet das als "dual issue". Weiterhin ist auch "Co-Issue" noch vorhanden. Co-Issue war ja, einen getrennten Farb- und Alphacombiner zu verwenden. NV40 kann sogar noch mehr.
Herkömmliche Co-Issue-fähige Pipelines können entweder einen Vektor4 bearbeiten oder separat Vektor3 sowie einen Skalar. Die NV40-Pipelines können auch in einer Vektor2 + Vektor-Konfiguration laufen. Dual- und Co-Issue kombiniert, kann die NV40-Pipeline bis zu vier Befehle abarbeiten – wobei sie nur eine einzelne arithmetische Allzweck-Unit hat. Wir werden jedoch weiterhin von Einheit 1 und 2 reden, weil es sich damit einfacher erklären lässt.
Wir möchten hier auf den Unterschied zwischen Shadereinheiten und tatsächlichen Recheneinheiten hinweisen. Pixelshader 2.0 und höher erfordern viele zusätzlichen Befehle, die sich nicht mehr rein mit Addition und Multiplikation ausführen lassen. Jede Allzweck-Shadereinheit muss auch Einheiten eingebaut haben, die solche "Spezialfunktionen" ausführen. Shadereinheiten haben intern also unterschiedliche Recheneinheiten, die aber aus verschiedenen Gründen nicht alle gleichzeitig genutzt werden können.
Vereinfachend gesagt, kann die NV30-Shadereinheit eine Gleitkomma-Instruktion pro Takt ausführen, während R300/R420 bis zu zwei schafft (mit einer "vertikalen Teilung"). Der NV40 kann bis zu vier Instruktionen pro Takt schaffen (mit einer "vertikalen" wie auch "horizontalen Teilung"). Allerdings muss man berücksichtigen, dass die "beiden" NV40-Shadereinheiten wie schon angesprochen in ihrem Befehlssatz begrenzt sind, so dass die durchschnittliche Auslastung in der Praxis geringer sein wird. Es gibt sogar noch weitere Begrenzungen, die den vollen Durchsatz verhindern.
Quad-basiertes Rendering
Was wir bisher als Pipeline diskutiert haben, ist keine Pipeline, das muss klar gesagt werden. Die tatsächliche Pipeline gibt es nur pro Quad, wobei ein Quad ein Block von 2x2 Pixeln ist. Beispielsweise kommen NV30 und NV35 mit gerade einmal einer einzigen Quad-Pipeline daher, jede kann dabei zeitgleich vier Pixel rendern. Derartige SIMD-Technologie ist stark verwandt mit den MMX-, 3DNow- bzw. SSE-Erweiterungen, die man in CPUs findet. Auf diese Weise spart man Steuer- und Datenfluss-Logik. Mehr noch, bestimmte Berechnungen lassen sich sowieso nur Quad-weise ausführen, weil sie auf ein einzelnes Pixel bezogen unsinnig sind. Dazu gehört zum Bespiel die Bestimmung vom LOD beim Textursampling (LOD steht für level of detail, gemeint ist der Detailgrad). GeForce 6800 Ultra und GT haben vier unabhängige Quad-Pipes, jede besitzt einen eigenen Pixel-Prozessor, der vier Pixel gleichzeitig in Bearbeitung hält.
"Ausführung in einem Takt" bedeutet nicht, dass im nächsten Takt das Ergebnis zur Verfügung steht. Jede Shader-Einheit hat interne Verteilungs-Mechanismen (Crossbars) und Recheneinheiten, die dann pro Takt ein neues Ergebnis liefern können – nach der "Ladezeit" der Pipeline. Die NV40-Pipe hat vielleicht eine Tiefe von etwa 256 Schritten. Das hieße, eine voll ausgelastete Quad-Pipe würde gleichzeitig an 256 Quads arbeiten. Da die 6800 Ultra mit 4 Quad-Pipes kommt und ein Quad aus vier Pixeln besteht, wären dann 4096 Pixel gleichzeitig in Bearbeitung, um die Pipeline-Latenzen zu verstecken. Unsere Zahlen stellen aber nur eine Vermutung dar.
Mehr zur Shadereinheit 1
Ab DirectX9 stehen interpolierte Vertexfarben und interpolierte (sowie perspektisch korrigierte) Texturkoordinaten als Input für jeden Befehl zur Verfügung. Diese Werte werden nur dann berechnet, wenn man sie braucht, aber dann immer gleich für das gesamte Quad. Dies trifft auf jeden Befehl zu. Gehen wir auf das Einzelpixel-Niveau und werfen einen näheren Blick auf Shadereinheit 1:
Shadereinheit 1
Von oben nach unten: Zwei unterschiedliche Quellen stehen als Input für die Pixelpipeline zur Verfügung (nämlich der Rasterizer oder der Pipeline-Loopback). Eine Crossbar (also Verteiler-Logik) leitet die angeforderten Werte zum zuständigen Interpolator, der in Einheit 1 sitzt. Wir wissen nicht, wie viele Interpolatoren die Hardware bietet (Shader Model 3.0 bietet aus logischer Sicht 10 Register für Interpolatoren, gegenüber 8 in Version 2.0 / 2.X). Shadereinheit 1 bietet eine SFU-Einheit (gelb, für Spezialfunktionen) und vier Multiplikations-Kanäle (blaugrün dargestellt). Dem folgt eine dedizierte Einheit für Textur-Operationen (in unserem Bild orange).
Eigentlich sind die Spezialfunktionen RCP und RSQ mit zwei unterschiedlichen Hardware-Einheiten realisiert worden, doch um das Bild einfach zu halten, abstrahieren wir das auf eine einzelne Einheit, die wir SFU#1 nennen. Die Shadereinheit insgesamt kann pro Takt nun zwei Befehle ausführen, entweder SFU und MUL3 ("3" steht hier für bis zu drei Komponenten) oder SFU und TEX (Textur-Operation). Wenn nur MUL gebraucht wird, können zwei unabhängige MULs ausgeführt werden, wobei jeder Taktzyklus auf vier Datenkanäle limitiert ist. MUL2 und MUL3 ist also nicht mehr in einem Takt möglich. Da nun einige Datenpfade gemeinsam genutzt werden, ist jede Unit auch in der Lage, einfach nur Daten weiterzureichen, womit die Unit in jenem Takt effektiv blockiert ist (da sie dann ja nicht rechnen kann). Das Ergebnis der Spezialfunktionen RCP und RSQ kann als Input für alle vier Multiplikations-Kanäle genutzt werden.
Die TEX-Einheit braucht als Input die Texturkoordinaten. Da diese Einheit selbst keinen Zugriff auf die Input-Register hat, muss Shadereinheit 1 die Werte durchleiten. Der Input für TEX-Operationen geht durch die MUL-Kanäle. Man kann diesen Input mittels einer Multiplikation noch modifizieren, ehe "im gleichen Takt" die TEX-Operation zum Zuge kommt. In den meisten Fällen bleibt wenigestens noch die skalare Spezialfunktion zur freien Nutzung übrig, sofern TEX gebraucht wird.
Die TEX-Einheit berechnet dann das LOD (den Detailgrad). Beide Werte, Texturkoordinate und LOD werden dann zur TMU übermittelt (die nicht eingezeichnet ist) welche nahe am Speicher-Controller sitzt. Die TMU übernimmt das tatsächliche Sampling aus der Textur und gibt das Ergebnis in einem Register zurück, das als Input für Einheit 2 verwendet werden kann. Da jedes Textursampling einige Takte Latenz nach sich zieht, führt die Pipeline in der Zwischenzeit Befehle für andere Quads aus.