
Unterstützung für Mesh Shaders und Meshlets in der Low-Level-API

In der neuesten stabilen Version von Evergine haben wir die Shader-Model-Unterstützung der Engine erweitert. Statt Shader Model 6.3 (in der Vorgängerversion) unterstützen wir nun Shader Model 6.7 und können so einen größeren Befehlssatz in unseren Shadern nutzen.
Das Spannendste an diesem Update ist jedoch, dass es den Weg für die Implementierung von Mesh Shaders in der Low-Level-Schicht von Evergine ebnet – mit einer einheitlichen Schnittstelle, die bereits unter DirectX 12 und Vulkan funktioniert.
Was sind Mesh Shaders?
Das Konzept der Mesh Shaders wurde 2018 von NVIDIA vorgestellt, aber erst 2020 mit DirectX 12 Ultimate und kurz darauf als Erweiterung in Vulkan umgesetzt.
Diese neue Pipeline sieht vor, renderbare Meshes in kleine Fragmente, sogenannte Meshlets, aufzuteilen. Das ermöglicht sehr interessante Optimierungen:
- Geringerer Speicherverbrauch.
- Effizientere Culling-Techniken (Frustum und Occlusion).
- Höhere Performance in komplexen Szenen, da die GPU eine wesentlich größere Anzahl von Meshes verarbeiten kann.
Mit anderen Worten: Mesh Shaders definieren neu, wie Geometrie auf der GPU erzeugt und verarbeitet wird, und ersetzen Teile der klassischen Rasterisierungspipeline durch einen kompakteren und flexibleren Ablauf.
Pipelines im Vergleich

Die klassische Rasterisierungspipeline besteht aus 3 Stufen mit fester Funktion und 5 programmierbaren Stufen, während die neue Pipeline mit Mesh Shaders auf 2 Stufen mit fester Funktion und 3 programmierbare Stufen reduziert ist.
Der Pixel Shader ist in beiden Pipelines weiterhin vorhanden. Der entscheidende Unterschied liegt in der Geometrieerzeugung, für die zwei neue Stufen hinzukommen:
- Task Shader (in DX12 auch als Amplification Shader bezeichnet, in Metal als Object Function).
- Mesh Shader (ersetzt und erweitert die Rolle von Vertex, Geometry und Hull Shaders).
Praxisbeispiel in Evergine
Zur Veranschaulichung: In Evergine benötigen Sie zum Zeichnen eines Dreiecks lediglich einen Mesh Shader und einen Pixel Shader.

Ergebnis:

Der Mesh Shader definiert die drei Vertices mit ihrer Position und Farbe und legt die Indizes für die Triangulierung fest. Anders als bei klassischen Vertex, Geometry und Hull Shaders haben wir hier deutlich mehr Freiheit – bis hin zur Möglichkeit, Meshes automatisch in Meshlets zu unterteilen.
Was ist ein Meshlet?
Ein Meshlet ist eine kleine Gruppe von Vertices und Dreiecken (idealerweise 64 Vertices und 126 Dreiecke), die die GPU sehr effizient verarbeiten kann.

Für die automatische Erzeugung dieser Gruppen gibt es mehrere Bibliotheken; zum Industriestandard hat sich jedoch MeshOptimizer von Arseny Kapoulkine entwickelt.
In Evergine haben wir ein C#-Binding für diese Bibliothek erstellt, das als NuGet-Paket verfügbar ist und mit dem Sie Ihre Meshes ganz einfach in Meshlets unterteilen können.
Anwendungsbeispiel mit unserem Binding:

Nach der Unterteilung des Meshes kann der Shader die Meshlet-Daten entpacken und optimal rendern.

Unser Mesh Shader, der ein Mesh aus seiner Meshlet-Sammlung rendert, sieht so aus:


Task Shader: Effizientes Culling
Der Task Shader ist optional, aber besonders interessant, da wir damit festlegen können, welche Meshlets an die Mesh-Shader-Stufe übergeben werden.
Damit lassen sich Occlusion-Culling-Techniken sehr einfach umsetzen, sodass nur die sichtbaren Meshlets gerendert werden. In großen Szenen mit vielen Objekten kann die Ressourceneinsparung erheblich sein.
Beispiel: ein Task Shader, der alle Meshlets in der linken Bildschirmhälfte verwirft und nur die in der rechten Hälfte rendert.


Demo und Open Source
Die neue Pipeline mit Mesh Shaders ist bereits in der Low-Level-Schicht von Evergine verfügbar – Sie können also sofort unter DX12 und Vulkan damit experimentieren.
Den vollständigen Quellcode der in diesem Artikel verwendeten Demo können Sie aus unserem Repository herunterladen:
Außerdem enthalten sind weitere Beispiele für die Unterteilung von Meshes in Meshlets und das Rendering mit dieser neuen Pipeline.



Fazit
Die Einführung von Mesh Shaders ist ein sehr wichtiger Schritt für Evergine, mit dem sich das volle Potenzial moderner GPUs ausschöpfen lässt. Mit dieser Technik können Sie mehr Geometrie effizienter rendern und verfügen zudem über Tools wie unser MeshOptimizer-Binding, um sofort mit Meshlets zu arbeiten.
