UE5 Heterogeneous Multi-GPU

Aus UnrealWiki
Version vom 5. September 2026, 14:53 Uhr von Elaina (Diskussion | Beiträge) (Projektstand 05.09.2026: asynchroner 128x96-Texture-Job, Pending/Ready und persistentes Target)

Hinweis: Projektstand: 5. 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 30202f7 auf Branch mgpu-experimental
Windows/D3D12-Proof-of-Concept Asynchroner 128×96-Worker-Texture-Job, GPU-Transfer, Pending/Ready-Ausgabe und persistente UE-Textur 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 MainTextureCS auf der Intel-GPU. Der synchrone Debugtest erzeugt eine 64 × 64 Pixel große RGBA8-Textur; alle 4096 shader-generierten Pixel wurden korrekt verifiziert. Der normale Runtime-Pfad verwendet inzwischen einen asynchronen 128×96-Test. Der Shader liest seine Dimensionen mit GetDimensions(), schützt überhängende Threads durch einen Bounds-Check und wird mit Dispatch(16,12,1) ausgeführt. Ein fester 64×64-Divisor ist damit aus dem produktiven Testpfad entfernt.

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. Breite, Höhe und Row Pitch werden dynamisch aus Worker-Ressource und Transport-Metadaten übernommen. Der erfolgreiche 128×96-RGBA8-Test verwendet 512 Byte Row Pitch und 49.152 Byte Transfergröße. 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. SubmitWorkerTextureComputeJob() reicht den Worker-Dispatch ohne CPU-Wait ein. QueueNormalTextureTransfer() reiht Wait, Kopie und Completion-Signal in Unreals D3D12-Kontext ein. Der normale MainTextureCS-Pfad enthält weder CPU-Fence-Waits noch Readback, Map oder Pixelvergleich. Erst TickNormalTextureTransfers() prüft die Primary-Completion in RHIEndFrame() non-blocking.

Eine passende Primary-FTextureRHIRef wird pro Worker und Konfiguration persistent gehalten. Nach Fence 2 kann der nächste Job dieselbe Resource erneut verwenden; dabei führt Unreal die Übergänge SRVMask -> CopyDest -> SRVMask aus. Pending- und Ready-Zustand hängen von Inhaltsgeneration und Fence ab, nicht von der Pointer-Identität. Eine native D3D12-Resource wird weiterhin nicht blind als UE-Textur gewrappt, weil Unreals Resource-State-Buchhaltung den tatsächlichen nativen Zustand sonst nicht sicher kennt.

Lebensdauer und automatisches Cleanup

Da die CPU nicht blockierend wartet, hält der Normal-Pfad einen vollständigen In-Flight-Job: Worker-Textur, Root Signature, PSO, Descriptor Heap, Allocator, Command List, Worker-Fence, Cross-Adapter-Transport, Shared-Ressourcen und Primary-Textur bleiben bis zum sicheren Abschluss erhalten. Die Completion wird automatisch in FD3D12DynamicRHI::RHIEndFrame() geprüft:

Worker-Fence vor dem Submit erzeugen
  -> Dispatch und UAV->COPY_SOURCE auf Worker-Queue
  -> Worker-Kopie in Shared Buffer
  -> Fence 1: Shared Buffer fertig
  -> Primary Wait, Copy und Signal Fence 2
  -> Output ist Pending
  -> RHIEndFrame prüft GetCompletedValue()
  -> Fence 2 erreicht: aktuelle Generation wird Ready
  -> Job sicher freigeben; persistentes Target bleibt erhalten

Mehrere Jobs können lifetime-sicher 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. Generationen verhindern, dass ein älterer Job einen neueren Pending- oder Ready-Output zurückrollt. Ein persistentes Target wird nicht erneut beschrieben, solange es noch in flight ist; dann wird vorläufig eine neue Textur erzeugt. Backpressure und ein echter Ressourcenpool fehlen noch.

Aktueller nächster Schritt

Der nächste qualitative Schritt ist ein erster sichtbarer Consumer: Das Worker-Ergebnis soll kontrolliert in einem UE-Material oder Compositing-Pfad erscheinen. Dabei muss insbesondere die Synchronisation stimmen, wenn dieselbe persistente Textur gelesen und später mit einer neuen Generation beschrieben wird. Danach folgen Backpressure, Ressourcenpooling, Messungen und echte Offload-Workloads.

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 asynchrone 128×96-End-to-End-Pfad ist über Dimensionen, Dispatch, Transfer, Fences, Pending/Ready und Reuse verifiziert; ein eigener 128×96-Pixel-Readback fehlt noch. Der synchrone 64×64-Debugtest prüft weiterhin alle 4096 Pixel.
  • 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 und Worker-Command-Objekte werden noch pro Job erzeugt. Backpressure, Pooling sowie Double-, Triple- oder Ring-Buffering fehlen.
  • 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 sichtbarer Material-/Renderer-Consumer, echte UE-Szenen-Workloads, 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. Worker-Ergebnis als ersten sichtbaren Consumer in Material, Compositing oder Render Graph einbinden.
  2. Consumer-Synchronisation für Updates derselben persistenten Textur absichern.
  3. Backpressure sowie Pools für Shared Heaps, Buffer, Fences und Command Objects einführen.
  4. Formate, Mips, Arrays/Slices und gegebenenfalls MSAA capability-basiert erweitern.
  5. Latenz, Bandbreite, Größen, Update-Raten und Synchronisationskosten messen.
  6. Ersten echten UE-Offload wie SceneCapture2D, CCTV, Spiegel, Minimap, Niagara oder unabhängigen Compute integrieren.
  7. Capability-Matrix, Benchmark-Wizard, Scheduler und manuelle Overrides entwickeln.
  8. Mehrere Worker-GPUs unterstützen.
  9. Fallback-Matrix aus Normal Shared Buffer, System-RAM/Staging und einem späteren Experimental Fast Path ergänzen.
  10. Linux/Vulkan mit getrennten Devices, External Memory und externer Synchronisation untersuchen.

Quellen