Übersicht
Godot Engine ist eine beliebte plattformübergreifende Open-Source-Game-Engine mit umfassender Unterstützung für Android. Mit Godot können Spiele nahezu jedes Genres erstellt werden. Es unterstützt sowohl 2D- als auch 3D-Grafiken. In Godot Version 4 wurde ein neues Rendering-System mit erweiterten Funktionen für High-Fidelity-Grafiken eingeführt. Der Godot 4-Renderer ist für moderne Grafik-APIs wie Vulkan konzipiert.
Die Godot Foundation hat die Grafikoptimierungsexperten von The Forge Interactive beauftragt und mit Google zusammengearbeitet, um den Godot 4-Vulkan-Renderer zu analysieren und weiter zu verbessern. Die Optimierungen wurden dann in das Projekt-Repository eingefügt. Mit diesen Optimierungen können Entwickler benutzerdefinierte Vulkan-Renderer auf Android-Geräten verbessern.
Optimierungsmethode und -ergebnisse
Für den Optimierungsprozess wurden zwei verschiedene 3D-Szenen in Godot als Benchmarking-Ziele verwendet. Die Renderingzeit der Szenen wurde bei jeder Optimierungsiteration auf mehreren Geräten gemessen. Damit sie aufgenommen werden konnten, mussten Änderungen am Renderer Leistungsverbesserungen auf mindestens einigen der getesteten Geräte zeigen und keine Leistungseinbußen auf einem Gerät verursachen.
Bei den Tests wurden mehrere gängige Android-GPU-Architekturen verwendet. Viele Optimierungen führten zu allgemeinen Verbesserungen, einige hatten jedoch einen größeren Einfluss auf bestimmte GPU-Architekturen. Die Summe aller Optimierungsarbeiten führte zu einer allgemeinen Verringerung der GPU-Framezeiten um 10 bis 20 %.
Allgemeine Vulkan-Optimierung
Das Forge-Team hat ein allgemeines architektonisches Refactoring am Godot-Vulkan-Rendering-Backend durchgeführt, um die Leistung zu verbessern und das Backend bei steigenden Anforderungen an das Rendern von Inhalten zu skalieren. Die Optimierungen sind nicht speziell für Mobilhardware gedacht, sondern kommen allen Godot-Vulkan-Plattformen zugute.
Unterstützung für dynamische UBO-Offset
Beim Binden eines Descriptor-Sets, das ein dynamisches Uniform-Buffer-Objekt (UBO) enthält, ermöglicht Vulkan die Angabe dynamischer Abweichungen in das UBO in den Bindungsparametern. Mit dieser Funktion können Daten für mehrere Rendering-Vorgänge in einem einzigen UBO verpackt und der Descriptor-Set mit einem anderen dynamischen Offset neu gebunden werden, um die richtigen Daten für den Shader auszuwählen. Der Godot-Vulkan-Renderer wurde aktualisiert, damit dynamische Offsets verwendet werden können, anstatt den Offset immer auf null zu initialisieren. Diese Verbesserung ermöglicht zukünftige Effizienzoptimierungen.
Pools mit linearen Descriptor-Sets
Bisher wurden im Godot-Vulkan-Renderer alle Descriptor-Set-Pools mit dem Flag VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT
erstellt. Das bedeutet, dass Descriptor-Set-Zuweisungen wieder an den Pool zurückgegeben und Neuzuweisungen aus dem Pool vorgenommen werden konnten. Dieser Modus führte zu einem zusätzlichen Overhead im Vergleich zu Descriptor-Set-Pools, die nur eine lineare Zuordnung und anschließend ein vollständiges Zurücksetzen des Pools zulassen.
Sofern möglich, werden Descriptor-Set-Pools jetzt als lineare Pools erstellt, ohne dass VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT
festgelegt wird. Lineare Pools werden dann bei Bedarf als Ganzes zurückgesetzt. Diese Arbeit umfasst auch eine zusätzliche Optimierung der Bindung von Batch-Beschreibungssätzen, wenn dies möglich ist, wodurch die Anzahl der einzelnen Aufrufe von vkCmdBindDescriptorSets()
reduziert wird.
Unterstützung für unveränderliche Sampler
Sampler-Objekte, die Daten zur Stichprobenbelegung enthalten, werden traditionell als Teil der Daten des Descriptor-Sets gebunden. Mit dieser Methode können Sampler-Objekte dynamisch in den Daten des Deskriptorsatzes ausgetauscht werden. Vulkan unterstützt auch unveränderliche Sampler, die die Samplerdaten direkt in das Layout des Descriptor-Sets codieren. Diese Samplerkonfiguration wird beim Erstellen des Deskriptorsatzes und des Pipelinestatus gebunden und kann nach der Erstellung nicht mehr geändert werden.
Bei unveränderlichen Samplern müssen keine diskreten Samplerobjekte mehr verwaltet und gebunden werden. Dafür ist die Flexibilität eingeschränkt. Der Godot-Vulkan-Renderer wurde aktualisiert, um die Verwendung unveränderlicher Sampler zu unterstützen. Die Sampler-Nutzung wurde so geändert, dass nach Möglichkeit unveränderliche Sampler verwendet werden.
Optimierung für Mobilgeräte
Zusätzliche Optimierungen wurden implementiert, um die Renderingleistung auf mobiler Grafikhardware speziell zu verbessern. Aufgrund unterschiedlicher Architekturdesigns sind die Optimierungen für Grafikhardware der Desktopklasse im Allgemeinen nicht relevant.
Zu den Optimierungen gehören:
- Verwendung großer Push-Konstanten ersetzen
- Lazy-Pufferzuweisung
- Unterstützung für nichtflüchtigen Puffer
- Änderung des ASTC-Dekodierungsmodus
- Vorab-Bildschirmdrehung
Verwendung großer Push-Konstanten ersetzen
Push-Konstanten sind eine Funktion, mit der Konstantenwerte für das aktive Shaderprogramm in den Befehlsbuffer eingefügt werden können. Push-Konstanten sind praktisch, da sie weder das Erstellen noch das Befüllen von Puffern erfordern und nicht an Deskriptoren gebunden sind. Push-Konstanten haben jedoch eine begrenzte maximale Größe und können sich negativ auf die Leistung auf Mobilhardware auswirken.
Bei Tests auf Android-Geräten wurde die Leistung durch das Ersetzen der konstanten Nutzung von mehr als 16 Byte durch einheitliche Puffer verbessert. Shader, die 16 Byte oder weniger Konstantendaten verwendeten, waren mit Push-Konstanten leistungsfähiger. Neben Leistungsüberlegungen haben einige Grafikhardware-Geräte ein Mindestalignment von 64 Byte für einheitliche Puffer, was die Speichereffizienz aufgrund von nicht verwendetem Padding im Vergleich zur Verwendung von Push-Konstanten verringert.
Lazy-Pufferzuweisung
Die meisten mobilen Grafikhardwares verwenden eine Tile-Based Deferred Rendering (TBDR)-Architektur. GPUs, die TBDR verwenden, unterteilen den größeren Bildschirmbereich in ein Raster aus kleineren Kacheln und rendern sie kachelweise. Jede Kachel wird von einer kleinen Menge Hochgeschwindigkeits-RAM unterstützt, die von der GPU zum Speichern verwendet wird, wenn die GPU eine Kachel rendert. Bei TBDR können Renderziele, die außerhalb ihres Rendering-Passes nie von einem anderen Ziel abgetastet werden, effektiv vollständig im Tile-RAM verbleiben und erfordern keinen Puffer für einen Back-Store im Hauptspeicher.
Das VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT
wurde beim Erstellen geeigneter Rendering-Ziele wie der Hauptfarb- und Tiefenziele hinzugefügt, um das Zuweisen von Pufferspeicher zu vermeiden, der nie verwendet wird. Die durch die verzögerte Arbeitsspeicherzuweisung in Beispielszenen erzielten Einsparungen betrugen bis zu etwa 50 Megabyte RAM.
Unterstützung für nichtflüchtigen Puffer
Bei Mobilgeräten wird eine einheitliche Speicherarchitektur (Unified Memory Architecture, UMA) anstelle einer Hardwaredifferenzierung zwischen Haupt-RAM und Grafik-RAM verwendet. Wenn Haupt-RAM und Grafik-RAM getrennt sind, müssen Daten vom Haupt-RAM in den Grafik-RAM übertragen werden, damit sie von der GPU verwendet werden können. Godot implementiert diesen Übertragungsprozess bereits in seinem Vulkan-Renderer mithilfe von Staging-Buffers. Bei UMA-Hardware ist für viele Datentypen kein Zwischenspeicher erforderlich. Der Arbeitsspeicher kann sowohl von der CPU als auch von der GPU verwendet werden. In Forge wurde die Unterstützung für persistente freigegebene Puffer auf unterstützter Hardware implementiert, um nach Möglichkeit das Staging zu vermeiden.
Änderung des ASTC-Dekodierungsmodus
Adaptive Scalable Texture Compression (ASTC) ist das bevorzugte moderne Texturkomprimierungsformat auf Mobilhardware. Während der Dekomprimierung kann die GPU standardmäßig texel in einen Zwischenwert decodieren, der eine höhere Genauigkeit als für die visuelle Wiedergabe erforderlich ist. Dies führt zu einer geringeren Texturierungseffizienz. Wenn die Zielhardware dies unterstützt, wird die Erweiterung VK_EXT_astc_decode_mode
verwendet, um bei der Dekodierung anstelle von 16‑Bit-Gleitkommawerten 8‑Bit-nichtnormalisierte Werte pro Komponente anzugeben.
Vorab-Bildschirmdrehung
Für eine optimale Leistung bei der Verwendung von Vulkan auf Android müssen Spiele die Geräteausrichtung des Bildschirms mit der Ausrichtung der Rendering-Oberfläche abgleichen. Dieser Vorgang wird als Vorrotation bezeichnet. Wenn die Vorrotation nicht durchgeführt wird, kann dies zu einer geringeren Leistung führen, da das Android-Betriebssystem einen Compose-Pass hinzufügen muss, um Bilder manuell zu drehen. Dem Godot-Renderer wurde die Unterstützung für die Vorrotation auf Android hinzugefügt.
Verbesserungen bei der Fehlerbehebung
Neben Leistungsoptimierungen hat The Forge die Fehlerbehebung bei Grafikproblemen im Godot-Renderer mit den folgenden Ergänzungen verbessert:
- Gerätefehler – Verlängerung
- Navigationspfade
- Debug-Markierungen
Gerätefehler – Verlängerung
Wenn bei der GPU während der Renderingvorgänge ein Problem auftritt, kann der Vulkan-Treiber ein VK_ERROR_DEVICE_LOST
-Ergebnis von einem Vulkan API-Aufruf zurückgeben. Standardmäßig werden keine zusätzlichen Kontextinformationen dazu bereitgestellt, warum der Fahrer VK_ERROR_DEVICE_LOST
zurückgegeben hat. Die Erweiterung VK_EXT_device_fault
bietet dem Fahrer die Möglichkeit, zusätzliche Informationen zur Art des Fehlers anzugeben. In Godot wurde die Unterstützung für die Aktivierung der Gerätefehlererweiterung (falls verfügbar) und für die Meldung von Informationen hinzugefügt, die vom Treiber zurückgegeben werden.
Navigationspfade
Ein GPU-Absturz oder eine Ausführungsblockierung kann schwierig zu beheben sein. Um besser nachvollziehen zu können, welche grafischen Inhalte zum Zeitpunkt eines Fehlers gerendert wurden, wurde dem Godot-Renderer die Unterstützung von Navigationspfaden hinzugefügt. Breadcrumbs sind benutzerdefinierte Werte, die Inhalten in Zeichenlisten in der Rendergrafik zugeordnet werden können. Breadcrumb-Daten werden geschrieben, bevor ein neuer Renderdurchlauf gestartet wird. Wenn ein Absturz oder eine Ausführungsverzögerung auftritt, können Sie anhand des aktuellen Breadcrumb-Werts ermitteln, welche Daten das Problem möglicherweise verursacht haben.
Debug-Markierungen
Debug-Markierungen werden, sofern vom Treiber unterstützt, zum Benennen von Ressourcen verwendet. So können benutzerlesbare Strings mit Vorgängen wie Rendering-Pässen und Ressourcen wie Buffers und Texturen verknüpft werden, wenn ein Grafiktool wie RenderDoc verwendet wird. Der Godot-Vulkan-Renderer unterstützt jetzt Anmerkungen für Debug-Markierungen.
Weitere Links
Godot Engine-Blog – Update zur Zusammenarbeit mit Google und The Forge
Pull-Request für die Vulkan-Unterstützung in der Godot Engine