- Zusammenfassung der Erfahrung, während drei Monaten Vulkan zu lernen und eine kleine Game-Engine mit zwei Demo-Spielen selbst zu implementieren
- Aufbauend auf bestehender OpenGL-Erfahrung die Komplexität von Vulkan schrittweise bewältigt und Kernfunktionen wie glTF-Loading, Skinning und Shadow Mapping implementiert
- Die Engine heißt EDBR (Elias Daler’s Bikeshed Engine), besteht aus rund 19.000 Zeilen Code und nutzt moderne Grafiktechniken wie bindless Descriptors, PVP und BDA
- Der Artikel teilt praktische Implementierungsdetails zu unverzichtbaren Bibliotheken wie vk-bootstrap, VMA, volk sowie zu Pipeline-Mustern, automatisierter Shader-Build-Pipeline und Synchronisationsverwaltung
- Durch den Umstieg auf Vulkan wurden globaler Status entfernt, explizite Kontrolle, eine bessere Debugging-Umgebung und Konsistenz zwischen GPUs erreicht; künftig sind Render Graph, SDF-Fonts und volumetrische Effekte geplant
Überblick über das Lernen von Vulkan und die Engine-Entwicklung
- Der Autor begann Grafikprogrammierung im Selbststudium und hatte bereits vor anderthalb Jahren Erfahrung damit, eine 3D-Engine mit OpenGL zu schreiben
- Die Vulkan-basierte Engine eignet sich für kleine levelbasierte Spiele und ist eher auf Lernen und Experimente als auf Effizienz ausgerichtet
- Zu Beginn wurde zunächst ein einfaches 3D-Spiel erstellt und anschließend wurden wiederverwendbare Teile herausgelöst und zu einer Engine gemacht
- Dass das Projekt in drei Monaten abgeschlossen werden konnte, lag daran, dass es keine allgemeine Engine, sondern eine Engine für einen konkreten Zweck war
Lernpfad für Grafikprogrammierung
- Für Einsteiger wird empfohlen, mit OpenGL zu beginnen und Dinge wie das Anzeigen texturierter Modelle, Blinn-Phong-Beleuchtung und Shadow Mapping zu lernen
- Als empfohlene Ressourcen werden learnopengl.com, Anton’s OpenGL 4 Tutorials und Vorlesungen von Thorsten Thormählen genannt
- Betont wird außerdem, wie wichtig das Verständnis von linearer Algebra (Vektoren, Matrizen, Quaternionen) zusammen mit aktuellem OpenGL-4.6-Übungsmaterial ist
Ratschläge gegen Bike-shedding
- Unnötige Überplanung und Abstraktion vermeiden und am Prinzip „nur das implementieren, was jetzt gebraucht wird“ festhalten
- Empfohlen wird der Ansatz: erst zum Laufen bringen, später verbessern
- Es ist effizienter, zuerst ein kleines Spiel fertigzustellen als direkt eine allgemeine Engine zu bauen
- Komplexen Code oder Strukturen anderer nicht einfach nachahmen, sondern mit einer einfachen Struktur starten
Warum Vulkan gewählt wurde
- Für AAA-Spiele wird meist DirectX genutzt, für macOS/iOS Metal und im Web WebGPU/WebGL
- Der Autor bevorzugt Open Source und Standardtechnologien und entschied sich für Vulkan, passend zum Ziel, kleine 3D-Desktop-Spiele zu entwickeln
- OpenGL wird nicht mehr weiterentwickelt und ist auf macOS eingestellt
- WebGPU ist zwar kompakter, hat aber Grenzen wie mangelnde Reife, funktionale Einschränkungen und fehlende Unterstützung für bindless und Push Constants
Der Lernprozess mit Vulkan
- Anfangs fühlte es sich so schwierig an, als würde man „praktisch selbst einen Grafiktreiber schreiben“,
doch durch Dynamic Rendering, vk-bootstrap und vkguide ist der Einstieg leichter geworden
- Wichtige Lernressourcen:
- vkguide.dev (praxisorientiert von den Grundlagen an)
- TU Wien Vulkan Lecture Series
- 3D Graphics Rendering Cookbook, Mastering Graphics Programming with Vulkan
- Im ersten Monat wurden bereits glTF-Loading, Compute-Skinning, Frustum Culling und Shadow Mapping implementiert
Struktur der EDBR-Engine und Frame-Verarbeitung
- Der Engine-Code umfasst etwa 19.000 Zeilen, dazu kommen 4.600 Zeilen für ein 3D-Spiel und 1.200 Zeilen für ein 2D-Plattformspiel
- Wichtige Rendering-Schritte:
- Compute-Skinning → Cascaded Shadow Mapping (4096×4096) → PBR-basierte Geometry Shading
- Depth Resolve → Post FX (tiefenbasiertes Nebel-Rendering) → UI-Rendering
- Alle Grafiksysteme wurden ausschließlich für Vulkan neu geschrieben und nicht mit altem OpenGL-Code vermischt
Praktische Tipps für die Vulkan-Entwicklung
Empfohlene Bibliotheken
- vk-bootstrap: vereinfacht Initialisierung und Swapchain-Setup
- Vulkan Memory Allocator (VMA): automatisiert die Speicherverwaltung
- volk: vereinfacht das Laden von Erweiterungsfunktionen
GfxDevice-Abstraktion
- Verwaltet
VkDevice, VkQueue, VmaAllocator usw. in einem einzigen Objekt
- Zuständig für Frame-Beginn/-Ende, Erzeugung von Images und Buffern sowie Verwaltung bindless Descriptors
Shader-Verwaltung
- Verwendung von GLSL, Vorabkompilierung nach SPIR-V zur Build-Zeit mit
glslc
- Mit CMake-
DEPFILE werden Shader bei Änderungen automatisch neu gebaut
Pipeline-Muster
- Jeder Rendering-Schritt ist als Pipeline auf Klassenebene getrennt (
init, cleanup, draw)
- Durch
VK_KHR_dynamic_rendering werden Render Passes und Subpasses weggelassen, wodurch die Struktur einfacher bleibt
Programmable Vertex Pulling + Buffer Device Address
- Ein einziges Vertex-Struct verarbeitet alle Meshes
- Der Shader greift per buffer_reference direkt auf Buffer zu, ein VAO ist nicht nötig
Bindless Descriptor
- Mit globalen Texture-Arrays (
textures[], samplers[]) erfolgt Sampling auf Basis von Texture-IDs
- Texture-IDs werden in der Material-Struct gespeichert und per Push Constants übergeben
Upload dynamischer Daten
- Pro Frame werden GPU-Buffer ausgetauscht oder Daten über CPU-Staging-Buffer übertragen
- Die Klasse
NBuffer verwaltet die Frames-in-Flight-Struktur
Ressourcenbereinigung und Synchronisation
- Es werden explizite
cleanup-Funktionen verwendet; statt automatischer Bereinigung in Destruktoren erfolgt manuelle Verwaltung
- Mit
vkCmdPipelineBarrier2 wird Speichersynchronisation zwischen Passes durchgeführt
- Ein Render Graph ist für die Zukunft geplant
Beispiele für Implementierungsdetails
Sprite-Rendering
- Mit bindless Textures und Instancing werden Tausende Sprites in einem Zug gerendert
- Die Struct
SpriteDrawCommand wird in einen GPU-Buffer hochgeladen, anschließend erfolgt vkCmdDraw(6, N)
- 10.000 Sprites werden in 315μs gerendert
Compute-Skinning
- Ein Compute-Shader führt Vertex-Deformation auf Basis von Bone-Matrizen und Gewichten aus
- Für jede Instanz wird ein eigener Output-Buffer erzeugt, der danach im Rendering identisch behandelt wird
Trennung von Spiel und Renderer
- Die Spiellogik nutzt entt ECS, der Renderer verarbeitet nur DrawCommand-Vektoren
- Render-Befehle werden über Aufrufe wie
drawMesh und drawSkinnedMesh erzeugt
Szenen-Loading und Prefabs
- Level werden in Blender als glTF aufgebaut, anhand von Namensregeln für Nodes werden Prefabs automatisch gespawnt
- Prefabs sind in JSON definiert und verweisen auf externe glTF-Dateien
MSAA, UI, ImGui
- MSAA x8 auf Basis von Forward Rendering wird eingesetzt
- Ein automatisches Layout-System wurde entwickelt, inspiriert von der Roblox UI API
- Um das sRGB-Problem von Dear ImGui zu lösen, wurde ein eigenes Vulkan-Backend geschrieben
Weitere Komponenten
- Für Physik wird Jolt Physics verwendet, außerdem entt für ECS, OpenAL-soft für Audio und Tracy als Profiler
Vorteile des Umstiegs auf Vulkan
- Durch das Entfernen globalen Status entsteht eine explizite und modulare Codestruktur
- Validation Layers und RenderDoc-Debugging erleichtern die Fehlersuche
- Mehr Konsistenz zwischen GPUs und Betriebssystemen sowie vorhersehbareres Verhalten als bei OpenGL
- Erweiterbarkeit durch neue Shader-Sprachen (slang, shady)
- Weniger Abstraktion und klarere Kontrolle über die Pipeline verbessern die Wartbarkeit
Ausblick
- Geplant sind SDF-Fonts, paralleles Image-Loading und Mipmap-Erzeugung, Bloom, volumetrischer Nebel, Animations-Blendings, Render Graph und AO
- Vulkan zu lernen ist schwierig, hat aber stark dabei geholfen, moderne Grafik-APIs und Engine-Design besser zu verstehen
1 Kommentare
Hacker-News-Kommentare
Seit meinem Beitrag vor einem Jahr hat sich meine Meinung zu Vulkan nicht wesentlich geändert.
Für Leute, die Low-Level-Kontrolle über die Grafik wollen, ist es vielleicht interessant, aber für mich war es wirklich eine qualvolle API in der Nutzung.
Ich will immer noch einmal selbst eine Game-Engine bauen, aber schon das initiale Setup von Vulkan macht mir weiterhin Angst.
Was ich will, ist so etwas wie eine 3D-Version davon, wie SDL mit 2D-Grafik umgeht.
Wenn man mit SDL 3D machen will, muss man am Ende doch auf OpenGL heruntergehen, und das ist nicht das Niveau, das ich mir wünsche.
Vielleicht ist WebGPU eine Alternative, mit der ich gern arbeiten könnte.
Ich habe damit auch eine Engine gebaut, bin aber am Ende wieder zu einer Vulkan-basierten Engine zurückgekehrt, weil ich mehr Kontrolle und Leistung wollte.
Trotzdem habe ich im SDL-GPU-Code Synchronisationsmuster gelernt, die mir in meiner Vulkan-Engine sehr geholfen haben.
wgpuist ein Mittelweg, der Abstraktion auf WebGPU-Niveau bietet.Es ist leistungsfähiger als OpenGL, aber man muss sich nicht selbst um Details wie Resource Barriers oder Layout Transitions kümmern.
Ein Teil des Bookkeepings wird zur Laufzeit übernommen, dafür gibt es Einschränkungen wie die Unterstützung nur einer einzigen Queue.
Vulkan ist schwierig, aber mit Erweiterungen, die von den großen Herstellern unterstützt werden, wird es deutlich besser.
Trotzdem gibt es weiterhin unnötige Komplexität, etwa weil man Detailkonfigurationen angeben muss, die Treiber dann ignorieren.
Im Moment fehlt eine mittlere API zwischen High-Level-Game-Engines und Low-Level-Vulkan/Metal.
Wer als Anfänger 3D-Grafik lernen will, braucht eine einfache API auf dem Niveau von „ein Dreieck zeichnen“, bei der man Konzepte wie Shader oder Buffer noch nicht kennen muss.
Die feingranulare Kontrolle von Vulkan brauchen nur sehr wenige Engine-Entwickler, für die meisten reicht das OpenGL-Niveau völlig aus.
3D hat weit mehr kombinierbare Elemente als 2D, deshalb ist es schwer, das mit einer einfachen Grafik-API abzudecken.
OpenGL hatte ursprünglich auch dieses Ziel, wurde am Ende aber ebenfalls komplex.
„bike shedding“ bedeutet, sich an Nebensächlichkeiten festzubeißen und dabei die wichtigen Dinge aus dem Blick zu verlieren.
Das im Originaltext Beschriebene liegt eher in Richtung Feature Creep oder Over-Engineering.
Damit ist gemeint, dass man den Projektfortschritt blockiert und sich nur auf das persönliche Vergnügen konzentriert.
„bike shedding“ wird oft damit erklärt, dass man „über die Farbe des Fahrradschuppens entscheidet, bevor das Haus fertig ist“.
Es hieß zwar, „mit einem Minecraft-Multiplayer-Klon die Engine-Entwicklung zu beginnen ist keine gute Idee“,
aber tatsächlich bauen viele Leute Minecraft-artige Spiele als ihr erstes Engine-Projekt.
Bei Voxel-Engines ist das eine Art „Hello, world“.
Der Beitrag von damals (2024) erreichte 625 Punkte und 260 Kommentare — Link zum Original
Vulkan war das schwierigste Stück Technik, das ich je gelernt habe.
Es ist so unintuitiv und voller Wiederholungsarbeit, dass es einem den Spaß am Programmieren nimmt.
Wenn du einfacher anfangen willst, würde ich OpenGL empfehlen, besonders die Versionen vor der Einführung von Shadern.
Allerdings drängt die Branche OpenGL zunehmend zurück.
Ich bin nur Tutorials gefolgt und habe Code abgeschrieben, ohne die Konzepte zu verstehen.
Deshalb bin ich auf WebGPU (Google Dawn) umgestiegen, und das war viel einfacher als Vulkan.
Nachdem ich dank der Einschränkungen von WebGPU die Konzepte verstanden hatte, war es deutlich leichter, wieder zu Vulkan zurückzukehren.
WebGPU hat keine Push Constants und leidet unter Pipeline-Explosion, aber bei Vulkan sind Synchronisation und Speicherverwaltung schwieriger.
SDL_GPU ist eine API auf ähnlichem Niveau und daher gut für den Einstieg.
Vulkan ist eine überentwickelte API.
Eine GPU-Speicherzuweisung, die in CUDA mit einer Zeile möglich ist, verlangt in Vulkan Unmengen an Boilerplate.
Modernes Vulkan ist deutlich besser geworden, hat aber noch einen langen Weg vor sich.
Ich hoffe, dass SDL3 oder wgpu zu einer Abstraktionsschicht werden, die diese Komplexität verringert.
Da Valve SDL3 unterstützt, halte ich diese Richtung für vielversprechend.
Man sollte sich zuerst fragen: „Muss Grafik multithreaded verarbeitet werden?“
Falls nicht, gibt es keinen Grund, Vulkan/DX12 zu verwenden.
Bis echte Performance-Probleme auftreten, ist es viel besser, OpenGL, DX11 oder eine Game-Engine zu nutzen.
Ich bin von 3D-/Game-Programmierung fasziniert und schaue oft einigen YouTubern beim Spieleentwickeln zu.
Aber im Vergleich zu Web-Apps oder DevOps ist das eine deutlich komplexere Welt.
Pixel-Shader, Compute-Shader, Geometrie, lineare Algebra, sogar PDEs tauchen dort auf.
TokyoSpliff YouTube-Kanal
Ich finde es gut, dass es heute als coole Sache gilt, hobbymäßig eine Game-Engine zu bauen.
Ich entwickle seit 10 Jahren an meiner eigenen Engine, und es war eine sehr lohnende Erfahrung.
Wenn man zum ersten Mal Grafikprogrammierung macht, ist es besser, mit OpenGL anzufangen.
Ich habe vor 23 Jahren die OpenGL-Tutorials von NeHe gelesen und halte sie bis heute für eines der am besten aufgebauten Lernmaterialien.
Zur Klarstellung: Ich bin nicht der Autor des Originalbeitrags und habe nur den Titel beibehalten.