UE5 Heterogeneous Multi-GPU

Aus UnrealWiki

Hinweis: Projektstand: 4. September 2026. Die beschriebenen Ergebnisse sind ein experimenteller Proof of Concept für Unreal Engine 5.8 unter Windows und Direct3D 12.

UE5 Heterogeneous Multi-GPU ist ein experimentelles Projekt zur gemeinsamen Nutzung unterschiedlicher Grafikkarten in Unreal Engine 5. Eine leistungsfähige GPU übernimmt das normale Rendering. Weitere NVIDIA-, AMD- oder Intel-GPUs sollen als unabhängige Worker klar abgegrenzte Aufgaben bearbeiten.

Das Ziel ist ausdrücklich kein klassisches SLI oder CrossFire. Die Anwendung verteilt Arbeit selbst und tauscht nur benötigte Ergebnisse zwischen den GPUs aus.

Projektstatus
Engine / Plattform Unreal Engine 5.8 Source Build / Windows / Direct3D 12
Testsystem Lenovo LOQ 17IRX10
Primary NVIDIA GeForce RTX 5060 Laptop GPU
Worker Intel UHD Graphics
Git-Stand Commit 4808b5b auf Branch mgpu-experimental
Windows/D3D12-Proof-of-Concept Worker-Compute, Texturtransport und non-blocking UE-Runtime-Ausgabe grundsätzlich nachgewiesen
Gesamtvision etwa 30–35 % (Scheduler, echte Workloads, N-GPU und Vulkan noch offen)

Grundprinzip

Intel Worker GPU
  -> MainTextureCS
  -> lokale Worker-Textur
  -> Shared Cross-Adapter Buffer
  -> Shared Fence / Queue Wait
  -> UE-eigene FTextureRHIRef auf der Primary

RTX Primary GPU
  -> normales UE5-Rendering
  -> übernimmt und verwendet das Worker-Ergebnis

Jeder Worker besitzt ein eigenes ID3D12Device, eigene Queues, Command Lists und Ressourcen. Ein späterer Scheduler soll Rechenleistung, Fähigkeiten, Transferkosten und gewünschte Aktualisierungsrate bewerten. Vorgesehene Aufgaben sind beispielsweise SceneCapture2D, Spiegel, CCTV, Minimap, Compute oder Niagara.

Verifizierter Entwicklungsstand

Worker-Compute

Ein echter, von Unreal kompilierter Global Compute Shader läuft auf der Intel UHD, während Unreal regulär über die RTX rendert. Der Test verdoppelt 64 Ganzzahlen auf dem Worker; 64 von 64 Ergebnissen wurden korrekt verifiziert. Damit ist echte GPU-Arbeit auf einem zweiten, herstellerfremden Device nachgewiesen.

Zusätzlich läuft inzwischen MainTextureCS auf der Intel-GPU. Der Shader erzeugt selbstständig eine 64 × 64 Pixel große RGBA8-Textur mit einem aus den Pixelkoordinaten berechneten Farbmuster. Alle 4096 shader-generierten Pixel wurden gegen das erwartete Ergebnis geprüft. Das frühere Zwischenziel, eine Textur direkt auf dem Worker zu erzeugen, ist damit erreicht.

Cross-Adapter-Speicher und Synchronisation

  • RTX und Intel öffnen denselben D3D12 Cross-Adapter Heap.
  • Ein gemeinsamer Buffer wurde mit 64 Testwerten erfolgreich geprüft.
  • Ein Shared Fence synchronisiert die Queues direkt von GPU zu GPU.
  • Die CPU vermittelt die GPU-Abhängigkeit nicht; Worker- und Primary-Queue verwenden Signal und Wait direkt auf der GPU.
  • Der separate Readback-Verifikationstest darf weiterhin blockieren. Der normale Runtime-Pfad wartet dagegen nicht auf GPU-Leerlauf.

Texturtransport

Direkte Cross-Adapter-Texturen sind auf dem Testsystem nicht beidseitig verfügbar: Die Intel UHD meldet Unterstützung für Row-Major Cross-Adapter Textures, die RTX 5060 Laptop GPU nicht. Deshalb verwendet das Projekt einen allgemeineren Buffer-Fallback.

Intel MainTextureCS
  -> Worker-Textur
  -> CopyTextureRegion
  -> Shared Cross-Adapter Buffer
  -> Worker signalisiert Shared Fence 1
  -> UE-Primary-Queue wartet auf Fence 1
  -> direkte Kopie in UE-eigene FTextureRHIRef
  -> Primary signalisiert Completion Fence 2

Der robuste Normal-Pfad kopiert die Worker-Textur über den Shared Buffer direkt in eine von Unreal erzeugte FTextureRHIRef. Der ältere Umweg über eine zusätzliche native RTX-Zwischentextur ist im Normal-Runtime-Test entfallen. Das Ergebnis wird pro Worker in einer Output Registry veröffentlicht und kann über GetNormalTextureOutputForWorker() abgerufen werden.

Non-blocking Runtime-Pfad

Die produktive Testlogik ist aus dem Konsolenkommando in eine gekapselte NormalPath-API verschoben. QueueNormalTextureTransfer() reiht Wait, Kopie und Completion-Signal in Unreals D3D12-Kontext ein. Ein SubmitAndBlockUntilGPUIdle ist im normalen Ablauf nicht mehr erforderlich.

Eine native D3D12-Resource wird dabei bewusst nicht blind als UE-Textur gewrappt. Unreal könnte sonst einen anderen Ressourcenstatus annehmen als den tatsächlich vorhandenen. Ein späterer direkter Fast Path bleibt experimentell und muss auf den sicheren Normal-Pfad zurückfallen können.

Lebensdauer und automatisches Cleanup

Da die CPU nicht mehr blockierend auf das Ergebnis wartet, müssen Heap, Buffer und Fence bis zum tatsächlichen Abschluss der Primary-Kopie am Leben bleiben. Eine In-Flight Registry hält deshalb jeden Transport über mehrere Frames und prüft ihn automatisch in FD3D12DynamicRHI::RHIEndFrame():

Transport anlegen und registrieren
  -> Worker Fence 1: Worker-Kopie abgeschlossen
  -> Primary Fence 2: UE-Zieltextur fertig beschrieben
  -> RHIEndFrame prüft GetCompletedValue()
  -> erst ab Fence 2 Ressourcen freigeben

Mehrere Transporte können gleichzeitig in flight sein. Im aktuellen Stand besitzt jeder Transport eine eigene Shared Fence sowie eigene Shared Heaps und Buffer; die Werte 1 und 2 sind daher transportlokal. Bei einem späteren Fence-Pool müssen Fence-Werte pro Fence monoton steigen. Ein gepoolter Buffer-Slot darf erst nach Abschluss der Primary-Kopie wiederverwendet werden, nicht bereits nach der Worker-Kopie.

Aktueller nächster Schritt

Breite und Höhe werden am Cross-Adapter-Transport bereits aus WorkerTexture->GetDesc() gelesen. Der SafeCopy-Zielpfad enthält jedoch noch Annahmen des verifizierten 64×64-Falls. Als nächster Test ist deshalb bewusst eine nicht quadratische 128×96-RGBA8-Textur vorgesehen. Sie deckt vertauschte Dimensionen und ungewollte Quadratannahmen früh auf.

Erst nach dieser Dimensions-Generalierung folgen weitere Formate, Mips, Arrays oder Slices sowie persistente Ressourcenpools, belastbare Benchmarks und ein erster echter UE-Szenen-Workload.

Historische Vorläufer

Die Grundidee heterogener Multi-GPU-Nutzung ist nicht neu. Bereits mit der Einführung von Direct3D 12 entstanden mehrere Versuche, integrierte und dedizierte GPUs gemeinsam zu verwenden. Eine allgemein einsetzbare Lösung für moderne UE5-Spiele hat sich daraus jedoch nicht entwickelt.

Microsoft und Epic: UE4 Elemental Demo (2015)

Microsoft zeigte eine angepasste Unreal-Engine-4-Version der Elemental-Demo, bei der eine NVIDIA-GPU und eine Intel-iGPU gemeinsam arbeiteten. Das erklärte Ziel war, ansonsten ungenutzte Grafikleistung in Hybrid-Systemen zu verwenden. Dieser Versuch ist der Grundidee des Projekts besonders ähnlich, blieb aber eine technische Demonstration.

Intel: D3D12 Multi-Adapter Sample (2015)

Intels Beispiel kombinierte eine GeForce 840M mit Intel HD Graphics 5500. Beide GPUs berechneten getrennte Teile einer einfach parallelisierbaren Raytracing-Szene. Cross-Adapter-Ressourcen und Shared Fences führten die Ergebnisse anschließend zusammen. Intel berichtete in diesem Test von nahezu halbierter Framezeit, warnte jedoch ausdrücklich davor, das Ergebnis unmittelbar auf reale Spiele zu übertragen.

Ashes of the Singularity

Die Nitrous Engine von Ashes of the Singularity setzte Direct3D-12-Explicit-Multiadapter in einem veröffentlichten Spiel ein. Dabei konnten auch unterschiedliche GPU-Modelle und Hersteller zusammenarbeiten. Das war ein wichtiger Praxisnachweis, blieb in der Spielebranche aber eine seltene Ausnahme.

Weitere verwandte Ansätze

  • Rise of the Tomb Raider erhielt explizite Direct3D-12-Multi-GPU-Unterstützung, vor allem für klassische Kombinationen ähnlicher GPUs.
  • NVIDIA VR SLI wies bei Virtual Reality jeder GPU ein Auge zu. Das verteilt unabhängige Ansichten, bleibt jedoch NVIDIA-spezifisch.
  • Unreal nDisplay kann Viewports oder Frustums auf weitere GPUs beziehungsweise Prozesse auslagern und das fertige Bild zur Ausgabe-GPU zurückführen. Der Einsatzbereich ist hauptsächlich Virtual Production.
  • Forschung und High-Performance Computing verwenden seit Jahren Scheduler für heterogene GPUs, allerdings überwiegend für Compute- und Datenverarbeitungsaufgaben statt für einen allgemeinen UE5-Echtzeitrenderer.

Abgrenzung dieses Projekts

Das Projekt erfindet Direct3D-12-Explicit-Multiadapter nicht neu. Sein eigener Ansatz liegt in der Kombination aus tiefer UE5.8-RHI-Integration, UE-kompilierten Shadern auf unabhängigen Worker-Devices, frei wählbaren Spiel- und Compute-Workloads, Capability- und Performance-Scheduler, mehreren Fallback-Pfaden sowie dem langfristigen Ziel beliebiger Hersteller und mehrerer Worker.

Vergleich mit anderen Multi-GPU-Verfahren

Verfahren Arbeitsweise Verhältnis zu diesem Projekt
SLI / CrossFire Treiber- beziehungsweise Verbundlösung für meist ähnliche GPUs; häufig wird die Bildarbeit verteilt. Dieses Projekt benötigt keinen herstellerspezifischen GPU-Verbund und weist Aufgaben ausdrücklich selbst zu.
AFR (Alternate Frame Rendering) GPU 1 rendert einen Frame, GPU 2 den nächsten. Das Projekt verteilt unabhängige Aufgaben statt aufeinanderfolgender Frames. Dadurch werden Frame-Abhängigkeiten und typisches AFR-Pacing vermieden.
SFR (Split Frame Rendering) Mehrere GPUs bearbeiten Bereiche desselben Frames. Erfordert enge Lastverteilung und viel Datenaustausch. Das Projekt bevorzugt vollständig abgrenzbare Workloads und überträgt deren Ergebnis.
Direct3D 12 Linked Multiadapter Mehrere vom Treiber verbundene GPUs erscheinen als Knoten eines logischen Adapters. Eignet sich eher für eng kompatible GPUs. Das Projekt verwendet unabhängige Devices (Unlinked/Explicit Multiadapter) und unterstützt dadurch heterogene Herstellerkombinationen.
Direct3D 12 Unlinked / Explicit Multiadapter Die Anwendung verwaltet getrennte Adapter, Ressourcen und Synchronisation selbst. Das ist die technische Familie des Projekts. Hinzu kommen die eigene UE5-RHI-Integration, Workload-Ziele, Fallbacks und der geplante Scheduler.
UE nDisplay mGPU / Multi-Process Separate GPUs rendern bestimmte Viewports oder Frustums, vor allem für Virtual Production; Ergebnisse werden zur Ausgabe-GPU kopiert. Ähnliche Idee der aufgabenweisen Trennung, aber für einen anderen Einsatzbereich. Dieses Projekt zielt auf allgemeine Spiel- und Compute-Workloads innerhalb der Engine.
Vulkan Device Groups Ähnliche physische GPUs können ein gemeinsames logisches Device bilden. Für beliebige heterogene GPUs meist ungeeignet, da Geräte einer Gruppe nahezu gleiche Eigenschaften besitzen müssen. Eine spätere Vulkan-Portierung benötigt daher wahrscheinlich getrennte Devices und External Memory/Semaphores.

SLI und CrossFire bezeichnen den Verbund; AFR und SFR beschreiben mögliche Verteilungsmethoden innerhalb solcher Systeme. Sie sind deshalb nicht vollständig getrennte Kategorien.

Vorteile der geplanten Variante

  • Herstellerunabhängig: NVIDIA, AMD und Intel können grundsätzlich kombiniert werden.
  • Vorhandene Hardware nutzen: Auch eine sonst wenig genutzte integrierte GPU kann geeignete Nebenaufgaben übernehmen.
  • Aufgaben statt Frames verteilen: Spiegel, Minimap oder Compute können mit eigener Auflösung und Aktualisierungsrate laufen.
  • Keine identischen GPUs erforderlich: Unterschiedliche Fähigkeiten können gezielt genutzt werden.
  • Kontrollierter Datenaustausch: Nur das benötigte Ergebnis muss zurück zur Primary-GPU.
  • Robuste Fallback-Idee: Wenn direkte Texturen nicht gemeinsam nutzbar sind, bleibt der Shared-Buffer-Pfad.
  • Erweiterbar: Scheduler, manuelles Mapping und mehrere Worker sind als spätere Stufen vorgesehen.

Nachteile und technische Risiken

  • Hoher Entwicklungsaufwand: Geräte, Ressourcen, Zustände, Fences, Fehlerfälle und UE-Lebenszyklen müssen selbst verwaltet werden.
  • Transfer kann den Gewinn aufzehren: Cross-Adapter Heaps liegen laut D3D12 nicht automatisch im schnellen lokalen VRAM. Bandbreite und Latenz müssen für jeden Workload gemessen werden.
  • VRAM wird nicht einfach addiert: Benötigte Ressourcen können auf mehreren GPUs vorliegen und zusätzlichen Speicher verbrauchen.
  • Langsame Worker können bremsen: Eine Aufgabe lohnt sich nur, wenn Rechengewinn größer als Übergabe-, Warte- und Kopierkosten ist.
  • Nicht jeder Workload ist unabhängig: Hauptansicht, Lumen, Nanite und stark gekoppelte Renderpässe besitzen viele Abhängigkeiten und sind schwieriger auszulagern.
  • Hardwareunterschiede: Formate, Shader-Funktionen, Queue-Fähigkeiten und Cross-Adapter-Support müssen pro GPU geprüft werden.
  • Wartungsrisiko: Eingriffe in private D3D12RHI-Dateien können bei Engine-Updates angepasst werden müssen.
  • Produktionsreife fehlt noch: Ressourcenpools, Timeouts, Device-Lost-Wiederherstellung, Scheduler und echte Szenentests sind offen.

Wann die Variante sinnvoll ist

Am aussichtsreichsten sind Aufgaben mit viel eigener Rechenarbeit, einem relativ kleinen Endergebnis und wenigen Abhängigkeiten zur Hauptszene. Gute frühe Kandidaten sind unabhängige Compute-Aufgaben sowie Scene Captures, die nicht in jedem Frame aktualisiert werden müssen. Weniger geeignet sind winzige Aufgaben mit großem Transfer oder Renderpässe, die ständig auf Ressourcen der Primary-GPU zugreifen.

Die entscheidende Regel für den späteren Scheduler lautet daher:

Worker-Gewinn > Vorbereitung + Datentransfer + Synchronisation + Rückintegration

Bekannte Grenzen des Proof of Concepts

  • Der SafeCopy-Zielpfad ist trotz dynamisch gelesener Worker-Dimensionen noch auf den verifizierten 64×64-Testfall zugeschnitten.
  • Der Normal-Transport unterstützt derzeit DXGI_FORMAT_R8G8B8A8_UNORM mit Sample Count 1; weitere Formate, Mips, Arrays, Slices und MSAA sind offen.
  • Shared Heaps, Buffer, Fences, Worker Allocators und Command Lists werden noch pro Transfer erzeugt. Für Produktion braucht es Pooling sowie Double-, Triple- oder Ring-Buffering.
  • Raw-D3D12-Shared-Ressourcen sind noch nicht vollständig in Unreals Residency-Management eingebunden.
  • Praktisch getestet ist Primary plus ein Worker. Die Architektur ist auf mehrere Worker ausgelegt, N-GPU ist aber noch nicht praktisch validiert.
  • Ein echter UE-Szenen-Workload, Benchmark-Wizard und Scheduler fehlen weiterhin.
  • Device Lost, Timeouts, Speicherdruck und die vollständige Fallback-Matrix müssen vor produktiver Nutzung gehärtet werden.
  • Linux/Vulkan mit getrennten Devices, External Memory und externer Synchronisation ist noch nicht implementiert.

Roadmap

  1. SafeCopy-Dimensionen vollständig generalisieren und 128×96 RGBA8 testen.
  2. Danach Formate, Mips, Arrays/Slices und gegebenenfalls MSAA capability-basiert erweitern.
  3. Shared Heaps, Buffer, Fences und Command Objects persistent halten; Ring- beziehungsweise Mehrfachpuffer einführen.
  4. Latenz, Bandbreite, Größen, Update-Raten und Synchronisationskosten messen.
  5. Ersten echten UE-Offload integrieren und das Worker-Ergebnis in Material, Compositing oder Render Graph verwenden.
  6. Capability-Matrix, Benchmark-Wizard, Scheduler und manuelle Overrides entwickeln.
  7. Mehrere Worker-GPUs unterstützen.
  8. Fallback-Matrix aus Normal Shared Buffer, System-RAM/Staging und einem späteren Experimental Fast Path ergänzen.
  9. Linux/Vulkan mit getrennten Devices, External Memory und externer Synchronisation untersuchen.

Quellen