- 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
gpuCreatePipeline aufzurufen
- 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
Noch keine Kommentare.