Hauptteil

2 Grafikhardware

In diesem Kapitel wird am Beispiel der GeForce 6 Serie von NVIDIA die Funktionsweise aktueller Grafikhardware beschrieben. Zunächst wird erklärt, wie die sich Grafikhardware in das gesamte Computersystem eingliedert. Danach werden die speziellen Eigenschaften heutiger Grafikkarten wie Shadow-Buffer und High-Dynamic-Range-Rendering kurz erläutert.

Einbindung der Grafikhardware in ein Computersystem

Der Prozessor moderner Computersysteme kommuniziert mit dem Prozessor der Grafikkarte, der GPU, über eine Grafikschnittstelle. Diese heißt heutzutage PCI-Express (moderner) oder AGP. Da über die Grafikschnittstelle alle Kommandos, Texturdaten und Vertex-Daten zur Grafikkarte gelangen, musste die Geschwindigkeit der Schnittstellen angepasst werden. Der AGP-Bus war ursprünglich 66 MHz schnell und 32 Bit breit. Er hatte eine Transferrate von 264 MB/sek. Dann folgte AGP 2x, 4x und 8x, der die Transferrate jeweils verdoppelte. PCI-Express wurde 2004 eingeführt, mit einer maximalen theoretischen Bandbreite von 4 GB/sek simultan in beide Richtungen. Trotz der enormen Leistungssteigerung kommt die Geschwindigkeit noch nicht an die der Speicherschnittstelle der GPU heran, die bis zu 35 GB/sek erreicht. Die Speicherschnittstelle der CPU erreicht bei 800 MHz Front-Side-Bus Geschwindigkeit nur 6,4 GB/sek. Dieser enorme Geschwindigkeitsvorteil der GPU-Speicherschnittstelle zeigt, dass Algorithmen, die auf der GPU laufen und von der Bandbreite Gebrauch machen, einen enormen Geschwindigkeitsvorteil bringen können.

Abb. 3.1 zeigt, wie sich die Grafikhardware in die Systemarchitektur eingliedert.

 

Abb. 3.1 Systemarchitektur eines PCs

Der nächste Abschnitt befasst sich mit der Grafikpipeline, und erklärt, wie die Eingabedaten von der CPU in den Frame-Buffer kommen. Die Grafikpipeline ist ähnlich der DirectX-Pipeline. Hier werden jedoch die technischen Aspekte in den Vordergrund gestellt.

Die Grafikpipeline

 

Abb. 3.2 Blockdiagramm der GeForce 6 Architektur

Kommandos, Texturen und Vertex-Daten werden von der CPU durch gemeinsame Puffer im Systemspeicher oder lokalem Frame-Buffer-Speicher empfangen. Ein Kommandostrom wird von der CPU geschrieben. Dieser initialisiert und modifiziert Zustände, sendet Kommandos zum Rendern und referenziert Textur- und Vertex-Daten. Die Kommandos werden interpretiert und eine Vertex-Fetch-Einheit liest die Vertices, die durch die Render-Kommandos referenziert werden.

Die Kommandos, Vertices, und Zustandsänderungen werden dann an die tiefere Stufe der Pipeline weitergereicht. Die Vertex-Prozessoren (oder Vertex-Shader) können von einem Programm auf jedes Vertex eines Objektes angewendet werden. Dies können Transformationen und andere Operationen sein, die der Programmierer spezifiziert. Die Vertex-Prozessoren können auf die Texturdaten zugreifen und alle Operationen werden mit 32-Bit Fließkomma-Genauigkeit (fp32) berechnet. Die Anzahl der Vertex-Prozessoren ist skalierbar und kann so leicht die Leistung der Grafikhardware steigern, was zu Lasten des Preises geht.

Weil Vertex-Prozessoren Zugriff auf Texturen haben, sind sie mit dem Textur-Cache verbunden. Zusätzlich gibt es einen Vertex-Cache. Dieser speichert Daten vor und nach der Verarbeitung durch einen Vertex-Prozessor und reduziert so Zugriffs- und Rechenansprüche. Wenn z.B. ein Vertex-Index zweimal in einem Draw-Befehl vorkommt, muss das Vertex-Programm nicht zweimal ausgeführt werden, sondern der gecachte Wert kann genommen werden.

Dann werden die Vertices in Primitive zusammengefasst. Dies sind Punkte, Linien oder Dreiecke. Der Cull/Clip/Setup-Block entfernt Primitive, die nicht sichtbar sind und schneidet Primitive ab, die das View-Frustum schneiden. Beim Setup werden Kanten- und Ebenengleichungen aufgebaut, um die Daten für die Rasterisierung vorzubereiten.

 

Abb. 3.3 Vertex-Prozessor

Die Rasterisierungs-Einheit berechnet, welche Pixel oder Samples (bei Multi-Sampling) von jedem Primitiv überdeckt werden. Es benutzt die Z-Cull-Einheit, um schnell Pixel zu verwerfen, die von Primitiven mit einer kleineren Tiefe überdeckt werden.

Danach werden die rasterisierten Daten an den Fragment-Prozessor (Pixel-Shader) weitergereicht und weiterverarbeitet. Die Fragment- oder Pixel-Pipelines, welche die Textur-und Pixelverarbeitung übernehmen, wenden ein Shader-Programm unabhängig voneinander auf je ein Fragment an. Je mehr Pixel-Pipelines es also gibt, desto mehr Pixel können gleichzeitig verarbeitet werden. Ähnlich wie bei den Vertex-Prozessoren sind sie auch skalierbar und die Texturdaten werden gecacht, um Bandbreite zu sparen und die Geschwindigkeit zu verbessern. Anders als bei den Vertex-Prozessoren arbeiten jedoch immer vier Pixel-Pipelines zusammen. Sie arbeiten auf vier, quadratisch angeordneten Pixelgruppen, die auch Quads genannt werden. So kann man direkt verschiedene LOD (Level-Of-Detail)-Texturstufen berechnen.

 

Abb. 3.4 Fragment-Prozessor

Der Fragment-Prozessor benutzt den Texturprozessor, um Daten aus dem Speicher zu holen und die Texturen gegebenenfalls zu filtern (bilinear, trilinear, anisotropisch). Die Textur-Einheit unterstützt viele verschiedene Datenformate (A8R8G8B8, DXT1, DEPTH16). Die Daten kommen dann beim Fragment-Prozessor im Format fp32 oder fp16 an. Eine Textur kann als 2- oder 3-dimensionales Feld von Daten angesehen werden, das von der Textur-Einheit an beliebigen Stellen gelesen werden kann und gefiltert wird, um eine kontinuierliche Funktion zu rekonstruieren.

Jeder Fragment-Prozessor hat zwei Shader-Einheiten, die fp32-Operationen ausführen. Die Fragmente können durch den Branch-Prozessor wieder zurückgeleitet werden, um eine weitere Serie von Operationen zu durchlaufen. Die Zurückführung geschieht einmal pro Taktzyklus. Der erste fp32-Shader kann z.B. für die perspektivische Korrektur von Texturdaten verwendet werden oder für allgemeine Multiplikationen. Insgesamt können acht mathematische Operationen pro Taktzyklus ausgeführt werden bzw. vier, wenn eine Textur in die erste Einheit geladen wird.

In einem letzten Schritt kann die Nebel-Einheit verwendet werden um Nebel ohne Performance-Einbußen zu überblenden. Dies kann in IEEE Fließkomma-Genauigkeit geschehen und benötigt zwei Multiply-Adds um dies effektiv zu tun. Da diese Gegebenheiten für Nebel ausreichend und effizient sind, wird dafür eine eigene Einheit verwendet. Dies ist ein gutes Beispiel für ein Trade-Off zwischen hoher Flexibilität programmierbarer Hardware und maximaler Performance für ältere Applikationen.

Die rasterisierten Pixel werden dann an die Z-Compare- und Blend-Einheiten weitergegeben, die Tiefentest, Stencil-Operationen und Alpha-Blending anwenden und die resultierende Farbe der Pixel in das Render-Target (Frame-Buffer oder andere Render-Surface) schreiben.

Das Speichersystem gliedert sich in vier unabhängige Partitionen, die aus DRAMs bestehen. Durch die Partitionierung kann das Speicher-Subsystem effizient arbeiten, unabhängig davon, ob große oder kleine Datenmengen verarbeitet werden. Alle gerenderten Surfaces werden im Grafikspeicher abgelegt, Texturen und Eingabedaten können auch im Arbeitspeicher gespeichert werden.

Fähigkeiten der GPU

Geometrisches Instancing

Instancing bietet die Möglichkeit, mehrere Objekte mit einem Direct3D-Befehl zu zeichnen. Das Hardware-Feature, welches Instancing ermöglicht, nennt sich Vertex-Stream-Frequency. Dies bedeutet, dass man in einer Schleife über eine Menge von Vertices laufen kann. Das ist sinnvoll, wenn man mehrere gleiche Objekte an verschiedenen Positionen zeichnen möchte.

Frühes Culling/Clipping

Dieses Feature führt dazu, dass das Culling sehr effizient vor dem Shading gemacht wird und das Clipping mit voller GPU-Geschwindigkeit realisiert wird.

Rasterisierung

Folgende Primitive können gerendert werden:

Point-Sprites

Linien mit und ohne Anti-Aliasing

Dreiecke mit und ohne Anti-Aliasing

Für alle Primitive ist Multisampling-Anti-Aliasing möglich.

Z-Cull

Die Z-Cull Einheit erlaubt das schnelle Entfernen von nicht sichtbaren Oberflächen (Hidden-Surface-Removal).

Occlusion-Query

Occlusion-Query erlaubt das Sammeln von Statistiken darüber, wie viele Fragmente den Tiefentest bestanden haben und sendet das Ergebnis zurück an die CPU.

Texturing

Es wird die bilineare, trilineare und anisotropische Filterung von 2D-Texturen und Cube-Maps in verschiedenen Formaten unterstützt. Des Weiteren wird für 3D-Texturen bilineares, trilineares, anisotropisches und quadrilineares Filtern mit und ohne Mip-Mapping unterstützt. Neben weiteren Textur-Formaten unterstützt die GeForce 6 auch Texturen in erweiterter Form, deren Seitenlänge keine Zweierpotenz ist.

Shadow-Buffer

Bei dieser Technik wird zunächst die Szene von der Lichtquelle aus in einen speziellen Z-Buffer gerendert. Während der Beleuchtungsphase wird der Shadow-Buffer als projektive Textur abgerufen und ein Z-Test mit einem der Distanz zur Lichtquelle entsprechenden Wert gemacht. Wenn der Distanzwert den Test besteht, ist er im Licht, ansonsten befindet er sich im Schatten.

High-Dynamic-Range-Rendering

Weil Texturen im Format fp16x4 (vier Komponenten, die jeweils durch ein 16-Bit-Float dargestellt werden) im Pixel-Shader gefiltert werden können, werden wesentlich genauere und größere Werte möglich. Dadurch kann man Überblendeffeke (High-Dynamic-Range), Motion-Blur und andere Effekte darstellen.

Shader-Model 3.0

Mit dem Shader-Model 3.0 wurde die Programmierbarkeit der Vertex- und Pixel-Shader enorm erweitert. So ist es nun z.B. möglich, 512 statische und 65535 Befehle im Vertex-Shader und 65535 statische und dynamische Befehle im Pixel-Shader zu benutzen. Außerdem wurden Sprünge optimiert und Schleifen erlaubt. Das Shader-Model 3.0 bietet noch viele weitere Features und wird mit DirectX 10 vom Shader-Model 4.0 abgelöst.

Lizenz

Managed DirectX Copyright © Andreas Zimmer. Alle Rechte vorbehalten.

Dieses Buch teilen