Auf dem Weg in ein Zeitalter ohne Graphics-API
(sebastianaaltonen.com)- In den vergangenen zehn Jahren haben Low-Level-Graphics-APIs wie DirectX 12, Vulkan, Metal die GPU-Leistung gesteigert, aber auch die Komplexität und die Wartungskosten stark erhöht
- Moderne GPUs unterstützen vollständige Cache-Hierarchien, 64-Bit-Zeiger und bindless Ressourcen, wodurch bisher komplexe Zustandsobjekte und Bindungsmodelle überflüssig werden
- Das vorgeschlagene Design vereinfacht die Rendering-Pipeline drastisch durch speicherbasierten Zugriff über C/C++-Zeiger und einen einzelnen 64-Bit-Root-Pointer
- Es entfernt PSO-Explosion, Resource Barriers und komplexe Binding-APIs und schlägt eine Struktur vor, die GPU-Speicher und Shader-Sprache direkt verbindet
- Dieser Ansatz ist eine für moderne GPU-Architekturen optimierte Next-Generation-API und zeigt die Richtung, die DirectX 13 oder Vulkan 2.0 einschlagen sollten
Der Wandel von Low-Level-Graphics-APIs
- 2013 wurden die AMD-GCN-Architektur der Xbox One und PS4 zum Standard in der AAA-Spieleentwicklung, woraufhin Low-Level-APIs wie Mantle, DirectX 12, Vulkan und Metal erschienen
- Das heißt: Sie wurden auf Basis der GPU-Architekturen um das Jahr 2013 herum entworfen
- Das bisherige DirectX 11/OpenGL stieß wegen Single-Thread-Rendering und hohem Treiber-Overhead an seine Grenzen
- Diese APIs reduzierten mit vorab kompilierten Pipeline State Objects (PSO) die Kosten von Draw Calls, erhöhten aber die Komplexität, weil sie nicht gut zur Struktur von Engines passten
- In der Folge entstand innerhalb der Engine eine weitere „Low-Level-Treiber-Schicht“, wodurch sich die Rolle von Graphics-Programmierern weiter ausdifferenzierte
Historischer Hintergrund: Warum wurde alles so kompliziert?
- Frühe GPUs basierten auf getrennten Speichern und fest verdrahteten Pipeline-Strukturen
- OpenGL und DirectX setzten auf zustands- und objektbasierte Designs, um die Vielfalt der Hardware zu abstrahieren
- Selbst bis DirectX 11 wurden Texturen und Buffer über opake Deskriptoren verwaltet
- Dieses Design wurde danach auch in spätere APIs gewissermaßen aus Trägheit übernommen
Die Diskrepanz zwischen modernen GPUs und APIs
- Aktuelle GPUs unterstützen konsistente Cache-Hierarchien, PCIe ReBAR, 64-Bit-Zeiger und bindless Textures
- Damit wird eine Struktur möglich, bei der die CPU direkt in den GPU-Speicher schreibt und die GPU sofort daraus lesen kann
- In einer solchen Umgebung sind Konstrukte wie PSOs, Descriptor Sets und Binding Tables nicht mehr nötig
- Durch das explodierende Wachstum von PSO-Caches werden Hunderte GB Cache benötigt, was Ladeverzögerungen und Stottern verursacht
- Eine neue API könnte diese veralteten Strukturen entfernen und zu einem einfachen zeigerbasierten Zugriff wechseln
Vereinfachung des GPU-Speichermanagements
- In bisherigem Vulkan/DirectX 12 ist nach der Erzeugung einer Ressource noch eine Abfrage der Heap-Kompatibilität nötig, was ineffizient ist
- Der vorgeschlagene Ansatz weist GPU-Speicher direkt über eine einfache API im Stil von gpuMalloc/gpuFree zu
- Die CPU kann den GPU-Speicher direkt mappen und initialisieren
- Große Datenmengen lassen sich per Kopierkommando übertragen, um DCC-Komprimierung und Swizzle-Verarbeitung auszuführen
- CPU-Mapping-Adresse und GPU-Adresse werden getrennt behandelt und über gpuHostToDevicePointer umgewandelt
Modernisierung von Daten und Shader-Sprache
- Verwendet wird eine C/C++-zeigerbasierte Shader-Sprache wie bei CUDA, Metal und OpenCL
- Über Wide Loads auf Strukturebene (128 Bit oder mehr) ist effizienter Speicherzugriff möglich
- ByteAddressBuffer oder Texel Buffer von DirectX sind nicht länger optimal
- GLSL/HLSL unterstützen keine Zeiger, wodurch ein Ökosystem wiederverwendbarer Shader-Bibliotheken fehlt, während sich CUDA mit reichhaltigen Bibliotheken weiterentwickelt hat
Root-Argumente und Datenstrukturen
- Ein GPU-Kernel erhält einen einzelnen 64-Bit-Zeiger als Eingabe und castet ihn auf eine Struktur
- CPU und GPU teilen sich denselben C/C++-Header, um Konsistenz der Datenstrukturen sicherzustellen
- Über die Schlüsselwörter const/restrict werden Compiler-Optimierungen gefördert und die unnötige Trennung zwischen UBO und SSBO entfällt
- Dabei werden Preloading in Scalar Registers und Dynamic Uniform Optimization moderner GPUs genutzt
Vereinfachung des Texture-Bindings
- Alle Texturen werden als Array aus 256-Bit-Deskriptoren (Heap) verwaltet, das von CPU und GPU direkt beschrieben werden kann
- 32-Bit-indexierter Zugriff unterstützt nicht-uniformes (non-uniform) Texture-Sampling
- Das ist einfacher als der Descriptor Heap in DirectX 12 SM 6.6 und ähnlich zu Vulkan
VK_EXT_descriptor_buffer - Texture-Objekterstellung, Upload und Sampling werden vollständig auf Basis von GPU-Speicherzeigern vereinheitlicht
Shader-Pipeline und Konstanten
- Die Pipeline-Erzeugung besteht schlicht darin, Shader-IR zu laden und dann
gpuCreatePipelineaufzurufen - Root Signatures, Descriptor Sets und Binding-Definitionen sind nicht erforderlich
- Statische Konstanten (strukturbasiert) ersetzen Shader-Spezialisierungskonstanten und mildern das Problem der explodierenden PSO-Kombinationen
- Konstantenstrukturen können GPU-Zeiger enthalten, sodass Laufzeitadressen direkt hartkodiert werden können
Vereinfachung von Barriers und Synchronisation
- Die ressourcenspezifischen Barrier-Listen heutiger APIs passen nicht zu modernen GPU-Strukturen
- Das vorgeschlagene Modell verwendet nur Bitfield-Flags auf Queue-/Stage-Ebene
- Mit gpuBarrier(before, after, hazard) wird das Modell vereinfacht; ein Resource Tracking ist nicht nötig
- Über gpuSignalAfter / gpuWaitBefore lässt sich eine GPU→GPU-Synchronisation ähnlich einem Timeline Semaphore umsetzen
Command Buffer und Rendering
- Es werden nur einmalige (transiente) Command Buffer verwendet; das komplexe Wiederverwendungsmodell von Vulkan entfällt
- Mit gpuBeginRenderPass / gpuEndRenderPass werden Render Targets gesetzt und Clears durchgeführt
- Es gibt keine automatischen Barriers zwischen Render Passes, wodurch paralleles Rendering und Optimierungen wie ein Depth Pre-Pass möglich werden
Vereinfachung der Raster-Pipeline
- Vertex/Pixel Shader greifen zeigerbasiert auf Daten zu, wodurch Binding-APIs entfallen
- GpuDepthStencilState und GpuBlendState werden vom PSO getrennt, um die Zahl der Kombinationen zu verringern
- Mobile GPUs unterstützen programmierbares Blending über Framebuffer-Fetch-Intrinsics
- Das PSO enthält nur noch minimale Zustände wie Topologie, Format oder Sample-Anzahl
Indirect Draws und GPU-Driven Rendering
- Alle Argumente (data, arguments) werden als GPU-Pointer übergeben
- gpuDrawIndexedInstancedIndirectMulti unterstützt Multi-Draws
- Die GPU kann Root-Daten und Draw-Argumente direkt selbst erzeugen und so vollständiges GPU-driven Rendering umsetzen
Tooling und Kompatibilität
- Eine zeigerbasierte Struktur lässt sich wie in CUDA-/Metal-Debuggern über Symbolinformationen nachverfolgen
- Durch virtuellen Speicher gibt es kein Sicherheitsproblem; bei fehlerhaften Zugriffen tritt ein Page Fault auf
- Wie bei MoltenVK oder Proton können bestehende DirectX-, Vulkan- oder Metal-APIs über eine Übersetzungsschicht kompatibel gemacht werden
Mindestanforderungen und Fazit
- Nvidia Turing (2018), AMD RDNA1 (2019), Intel Xe1 (2022) und Apple M1 (2020) unterstützen alle die vorgeschlagenen Funktionen
- Heutige GPUs haben sich bereits zu einer Architektur mit bindless Ressourcen, 64-Bit-Zeigern und konsistentem Cache entwickelt
- Nur die APIs hängen noch an Abstraktionen aus der Vergangenheit fest
- Eine neue API wäre einfacher als DirectX 11, schneller als Vulkan und flexibler als Metal
- Vulkan 2.0 / DirectX 13 der nächsten Generation sollten zu einem solchen vollständig bindless Design wechseln und das Ökosystem statt mit HLSL/GLSL mit einer C/C++-zeigerbasierten Shader-Sprache erweitern
1 Kommentare
Hacker-News-Kommentare
Dieser Artikel zeigt hervorragend die unnötigen Teile von Vulkan und DX12 auf
Heutzutage unterstützt DX12 nicht einmal mehr Buffer-Pointer, sodass es praktisch vernachlässigt wirkt, und auch Vulkan wurde nicht in 2.0 aufgeräumt, weshalb es viele Treiber gibt, bei denen Erweiterungen nicht korrekt implementiert sind
Wenn es eine solche neue API gäbe, könnte man OpenGL darauf deutlich schneller emulieren, und auch so etwas wie SDL3 GPU könnte wohl eine 3- bis 4-fache Leistungssteigerung erreichen
Selbst Frank Lunas Buch behandelt die neuesten Funktionen nicht, und man muss Learn, GitHub-Beispiele und Referenzdokumente durchforsten
Vulkan ist ähnlich komplex, und selbst wenn 2.0 erscheint, ist fraglich, wie man es auf wichtigen Plattformen wie Android tatsächlich nutzen soll
Abgesehen von Intel Arc funktionieren die meisten GPUs auch ohne reBAR, aber Microsoft oder Intel müssten das wohl im UEFI erzwingen, damit Funktionen wie bindless texture stabil nutzbar sind
Allerdings würde das eine Untergrenze für unterstützte Hardware schaffen, und auf Mainboards von vor 2020 ist die Unterstützung uneinheitlich
Die eigentliche Motivation des Artikels fehlt
Laut dem Blog-Index geht es darum, dass „die Komplexität von Grafik-APIs und Shader-Sprachen in den letzten zehn Jahren stark zugenommen hat und nun die Abstraktionsschicht vereinfacht werden muss, um Entwicklungseffizienz und Leistung zu steigern und sich auf künftige GPU-Workloads vorzubereiten“
SSDs nutzten anfangs IDE-/SATA-Schnittstellen weiter, aber erst als man die Annahmen rotierender Festplatten verwarf und einen neuen Übertragungsweg schuf, konnte man die echte Leistung ausschöpfen
Die Analogie scheint zu sein, dass auch Grafik-APIs nun solche Legacy-Beschränkungen abwerfen müssen
Ich verfolge die Arbeit von Sebastian Aaltonen schon lange und bin daher vielleicht voreingenommen, aber dieser Artikel ist wirklich hervorragend
Ich denke, die Richtung der Zukunft ist eine Rückkehr zum Software-Rendering
Der Unterschied ist diesmal allerdings, dass die Algorithmen und Datenstrukturen hardwarebeschleunigt werden
In der VFX-Branche läuft diese Entwicklung bereits, und dass OTOY vor etwa fünf Jahren OctaneRender auf CUDA-Basis portiert hat, ist ein Beispiel dafür
Shader-Typen dienen dazu, die Lücken zwischen solchen Pipelines zu überbrücken, daher ist eine vollständige Softwareisierung nicht realistisch
Nanite in der Unreal Engine verwendet zum Beispiel beim Verarbeiten kleiner Dreiecke einen Software-Rasterizer, der über den Compute Shader der GPU läuft
Die Details des Artikels waren beeindruckend
Ich habe nur einen Teil verstanden, aber es wirkt wie ein künftiges Referenzwerk für das Design von Grafik-APIs
Für die meisten Gamer waren Vulkan/DX12 kein großer Gewinn, und viele Spiele hatten wegen PSO-Problemen zu kämpfen
Vulkan wird zwar verbessert, aber WebGPU hat die Beschränkungen des frühen Vulkan-Designs unverändert geerbt
In einer Situation, in der sich die Hardware schnell weiterentwickelt, war der Gang zu einer zu Low-Level-API vielleicht ein Fehler
Möglicherweise wäre ein stärker auf General-Purpose-Computing ausgerichteter Ansatz wie CUDA sogar der bessere Weg
Ich merke, dass ich Mantle vermisse
Es hatte auch Nachteile, aber es fühlte sich an, als würde man die Hardware direkt anfassen, und die Entwicklung für die Xbox 360 hat mir am meisten Spaß gemacht
Sie wurde von Nvidia und Nintendo entworfen, und ich halte sie für die intuitivste API unter den Konsolen
Nach der Lektüre dieses Artikels habe ich das Gefühl, einem historischen Moment beigewohnt zu haben
Ich musste an Google Toucan denken, das wie ein verwandtes Projekt wirkt
Dieser Artikel hat viele alte Erinnerungen in mir geweckt
Zusätzliche Aspekte, die API-Designer berücksichtigen, sind unter anderem
Ich frage mich, warum Microsoft keine neue DirectX-Version herausbringt
DirectX Ultimate oder 12.2 sind im Grunde ebenfalls DX12
Liegt es daran, dass durch Middleware wie die Unreal Engine die Bedeutung einer eigenen API gesunken ist, oder müsste EPIC eine neue API vorschlagen?
Tatsächliche Spieleentwickler bauen ein RHI (Rendering Hardware Interface) und konzentrieren sich auf die Spielentwicklung
Raytracing und Mesh Shader waren die größten Innovationen, werden aber noch immer wenig genutzt, sodass es dort scheinbar nicht weitergeht
Durch die Zentralisierung rund um Engines wie Unreal und Unity ist das Interesse an API-Innovationen gesunken, und Fortschritte finden nur noch auf der GPU-Seite statt
CPU-APIs sind weiterhin kaum mehr als simples Buffer-Mapping
Wie damals beim Aufkommen der Tessellation Shader scheint es wieder eine neue Hardware-Veränderung zu brauchen, damit es weitergeht
Ich frage mich, ob Valve möglicherweise eine eigene Grafik-API für SteamOS entwickeln wird
Soweit ich gehört habe, war Valve in der frühen Entwicklungsphase von Vulkan einer der wichtigsten Treiber