UE5 Heterogeneous Multi-GPU
Hinweis: Projektstand: 7. September 2026, Commit 43a5c07. 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.
| 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 43a5c07 auf Branch mgpu-experimental
|
| Windows/D3D12-Proof-of-Concept | Sichtbarer 128×96-Worker-Texture-Stream im TPS sowie isolierte Diagnose der Primary-Queue-/Submission-Latenz nachgewiesen |
| Gesamtvision | etwa 30–35 % (Scheduler, echte Workloads, N-GPU und Vulkan noch offen) |
Grundprinzip
Intel Worker GPU -> MainTextureCS + PatternPhase -> persistente Worker-Textur -> persistenter Shared Cross-Adapter Buffer -> monotone Shared Fence / Queue Wait -> persistente UE-eigene FTextureRHIRef -> stabile UExperimentalMultiGPUTexture RTX Primary GPU -> normales UE5-Rendering -> TPS-Material zeigt das Worker-Ergebnis sichtbar an
Pro Worker ist maximal ein Runtime-Job gleichzeitig in flight. Backpressure lehnt weitere Requests als Busy ab, bis der Slot nach der Primary-Completion wieder sicher verwendbar ist.
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. PatternPhase erzeugt für jeden akzeptierten Submit deterministisch einen neuen sichtbaren Inhalt. Abgewiesene Busy-Requests erhöhen die Phase nicht und erzeugen daher keine Lücken.
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 persistenter Shared Fence synchronisiert die Queues direkt von GPU zu GPU. Seine Werte steigen pro Job monoton: WorkerReady/PrimaryComplete verwenden die Paare 1/2, 3/4, 5/6 und so weiter.
- Die CPU vermittelt die GPU-Abhängigkeit nicht; Worker- und Primary-Queue verwenden
SignalundWaitdirekt 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 -> persistente Worker-Textur -> CopyTextureRegion -> persistenter Shared Cross-Adapter Buffer -> Worker signalisiert WorkerReady (1, 3, 5, ...) -> UE-Primary-Queue wartet auf WorkerReady -> direkte Kopie in persistente UE-eigene FTextureRHIRef -> Primary signalisiert PrimaryComplete (2, 4, 6, ...)
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. Die Output Registry trennt Persistent-, Pending- und Ready-Zustände sowie Inhaltsgeneration und Texturidentität. Dadurch kann eine verspätete Completion keinen neueren Output zurückrollen.
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. TickNormalTextureTransfers() prüft die Primary-Completion non-blocking über einen Runtime-Pump; RHIEndFrame() bleibt als Fallback erhalten.
Eine passende Primary-FTextureRHIRef wird pro Worker und Konfiguration persistent gehalten. Nach PrimaryComplete kann der nächste Job dieselbe Resource erneut verwenden; dabei führt Unreal die Übergänge SRVMask -> CopyDest -> SRVMask aus. Eine backend-neutrale RHI-Provider-API reicht die Ready-Textur an die Engine weiter. UExperimentalMultiGPUTexture hält eine stabile UTexture samt TextureReference und bindet die persistente FTextureRHIRef ohne zusätzlichen Pixel-Copy. 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.
Sichtbarer TPS-Consumer
AExperimentalMultiGPUDisplayActor zeigt die Worker-Ausgabe im Third-Person-Testlevel über den Materialparameter MultiGPUTexture an. Damit ist der Output nicht mehr nur über Logs oder Readback nachgewiesen: Die Intel UHD erzeugt fortlaufend die Texturinhalte, während die RTX die Spielwelt und das Material rendert. TextureConsumerCS kann Ready-Ausgaben auf der Primary zusätzlich als SRV lesen und fünf quantisierte Samples im Debugpfad prüfen.
Lebensdauer und automatisches Cleanup
Der Normal-Pfad verwendet pro Worker genau einen persistenten Runtime-Slot. Worker-Textur, Root Signature, PSO, Descriptor Heap, Command Allocator/List und Worker-Fence werden nach der ersten kompatiblen Initialisierung wiederverwendet. Dasselbe gilt für Shared Heap/Buffer/Fence und die Transport-Command-Objekte. Der In-Flight-Job trägt nur noch die nötigen Job-, Generation- und Fence-Metadaten.
Runtime Request -> Completion non-blocking prüfen -> falls Slot frei: Worker Dispatch -> WorkerReady-Fence signalisieren -> Primary Wait, SafeCopy und PrimaryComplete-Signal -> Output Pending -> Runtime-Pump oder RHIEndFrame prüft GetCompletedValue() -> Output Ready publizieren -> Slot wiederverwendbar
Backpressure erlaubt maximal einen echten In-Flight-Job pro Worker. Ein Busy-Request setzt keine GPU-Arbeit ab, setzt keine Ressourcen zurück und erhöht PatternPhase nicht. Worker-Fence-Werte steigen mit jedem akzeptierten Submit 1, 2, 3 und so weiter; der persistente Shared Fence verwendet fortlaufend die Paare 1/2, 3/4, 5/6. Fehler nach Übergabe von Ressourcen an die GPU führen zu sicherem Retire beziehungsweise Retention statt zu vorzeitigem Reuse.
Texture Stream und Benchmarking
Der projektseitige Texture Stream akzeptiert eine beliebige positive, endliche Zielrate. Punkt und Komma sind als Dezimaltrenner möglich. Optional lässt sich die Zahl erfolgreicher Submits begrenzen; Busy- oder Failed-Versuche verbrauchen dieses Limit nicht. Der Quiet-Modus unterdrückt per-Job-Ausgaben, behält aber Start, echte Fehler und die Abschlussstatistik bei.
Die Statistik unterscheidet Timer-Ticks, angenommene Submits, Busy/Failed, Ready, In-Flight und die jeweiligen effektiven Raten. Diagnosezeitpunkte erfassen unter anderem RequestAccepted, RenderCommandExecution, WorkerSubmit, NormalTransferQueued und ReadyObserved.
| Test | Ergebnis | Aussage |
|---|---|---|
| 5 Hz, 20 Submits | etwa 5 Ready/s, 20/20, kein Busy | Entspannter Single-Slot-Betrieb |
| 1000 Hz, 100 Submits, quiet | 100/100 Ready, 0 Fehler, etwa 58 Ready/s | Backpressure begrenzt den Durchsatz sicher |
1000 Hz bei t.MaxFPS 30
|
etwa 28–29 Ready/s trotz rund 982 Timer-Ticks/s | Primary-/SafeCopy-/Ready-Kette ist effektiv framegekoppelt |
Ein zusätzlicher non-blocking Aufruf von ImmediateFlush(DispatchToRHIThread) erreicht hohe Render-Command-Raten, hebt die gemessene Frame-Kopplung aber nicht auf. Im 30-FPS-Test lag der RHIEndFrame-spezifische Completion-Takt praktisch exakt bei 30 Hz. Das aktuelle Problem ist damit eingegrenzt: Nicht der Stream-Timer, sondern die Primary-RHI-/D3D12-Submission der SafeCopy-Kette bestimmt die sichtbare Ready-Rate.
Queue-Submission-Diagnose
MultiGPUD3D12QueueProbe untersucht die Submission unabhängig vom sichtbaren Texturpfad. Der Probe besitzt die Modi full, waitonly und signalonly sowie die Queue-Auswahl graphics, copy und native. Graphics und Copy verwenden RHIRunOnQueue(..., false) auf UE-verwalteten Queues. Native verwendet derzeit ausschließlich im Signal-only-Modus eine eigene persistente D3D12-DIRECT-Queue auf dem Primary-Device.
Der Probe misst Request, Worker-Submit, RHIRunOnQueue-Aufruf und -Callback, Primary-Execute, Fence-Completion sowie Ready bis zum nächsten akzeptierten Submit. Er fügt keine CPU-Waits, Readbacks, Maps oder BlockUntilGPUIdle ein und beschreibt keine Textur des sichtbaren Normal-Pfads.
| Signal-only-Test | PrimaryExecute bis Ready | Befund |
|---|---|---|
| UE Graphics Queue, 30 FPS | 33,319 ms | skaliert praktisch mit der Frame-Dauer |
| UE Graphics Queue, 60 FPS | 16,377 ms | skaliert praktisch mit der Frame-Dauer |
| UE Graphics Queue, 120 FPS | 8,712 ms | skaliert praktisch mit der Frame-Dauer |
| UE Copy Queue, 30 / 120 FPS | 32,593 / 8,206 ms | Copy Queue ist ebenfalls framegekoppelt |
| Private native DIRECT Queue, 30 FPS | durchschnittlich 0,014 ms; 0,001–0,037 ms | D3D12, Treiber und Primary-GPU sind nicht an die Frame-Dauer gebunden |
Der RHIRunOnQueue-Callback selbst erreicht den D3D12-Submission-Thread typischerweise bereits nach etwa 0,02–0,05 ms; die Verzögerung entsteht danach in der verwalteten Queue-/Submission-Reihenfolge. SubmitCommandsHint() ist in UE 5.8 nur ein veralteter Alias für ImmediateFlush(DispatchToRHIThread) und stellt daher keinen stärkeren Submission-Mechanismus dar.
Der native Test ist ein Architekturbeweis, noch kein Experimental- oder Direct-Modus. Er signalisiert nur einen privaten Fence und verwendet weder WorkerReady-Wait noch Cross-Adapter-Copy, UE-eigene Textur oder Engine-State-Tracking. Obwohl sein Fence nach rund 0,014 ms fertig ist, erreicht der gesamte Probe bei 30 FPS nur etwa 30,75 Ready/s; Ready -> NextSubmit dauert im Mittel 32,403 ms. Damit ist als nächster Engpass der Lifecycle beziehungsweise die Freigabe des Single Slots isoliert.
Test- und Diagnosebefehle
| Befehl | Zweck |
|---|---|
ExperimentalMultiGPU.TestComputeShader
|
Führt den vollständigen Worker-Compute-Testpfad aus. Er enthält auch ältere Debug- und Regressionstests mit CPU-Readback und ist daher nicht mit dem reinen asynchronen Runtime-Pfad gleichzusetzen. |
ExperimentalMultiGPU.TestTextureConsumer
|
Testet TextureConsumerCS auf der Primary-GPU. Die aktuelle Ready-Textur wird über UE-RHI gelesen und anhand ausgewählter Testpixel per Debug-Readback geprüft.
|
ExperimentalMultiGPU.TestTextureBridge
|
Prüft die Bridge von der fertigen FTextureRHIRef zu UExperimentalMultiGPUTexture und damit den UE- und materialtauglichen Texturpfad.
|
ExperimentalMultiGPU.SpawnDisplayActor
|
Spawnt den Test-Actor mit Plane im Spiel. Das Material zeigt darauf die vom Worker erzeugte Textur an. |
ExperimentalMultiGPU.StartTextureStream
|
Startet den sichtbaren Normal-Pfad: Worker-Textur → Cross-Adapter-Transfer → SafeCopy → UE-Textur → Material. |
ExperimentalMultiGPU.StopTextureStream
|
Verhindert weitere Submits des laufenden Streams. Ein bereits eingereichter GPU-Job darf noch sicher fertig werden. |
ExperimentalMultiGPU.StartQueueProbe
|
Startet den Diagnose-Benchmark für Primary-Queue-, Fence- und Submission-Verhalten. Der Probe verändert die sichtbare Textur nicht. |
Texture Stream
Grundsyntax:
ExperimentalMultiGPU.StartTextureStream [Hz] [MaxSuccessfulSubmits] [quiet]
| Beispiel | Wirkung |
|---|---|
ExperimentalMultiGPU.StartTextureStream
|
Standardmäßig etwa 5 Hz, ohne Submit-Limit. |
ExperimentalMultiGPU.StartTextureStream 37.5
|
Fordert 37,5 Hz ohne Submit-Limit an. Sowohl 37.5 als auch 37,5 werden akzeptiert.
|
ExperimentalMultiGPU.StartTextureStream 1000 100
|
Fordert 1000 Hz an und stoppt nach exakt 100 erfolgreichen Submits. |
ExperimentalMultiGPU.StartTextureStream 1000 100 quiet
|
Wie zuvor, unterdrückt aber Per-Job- und Busy-Logspam. |
Die Hz-Angabe ist eine Ziel- beziehungsweise Request-Frequenz. 1000 bedeutet daher nicht automatisch 1000 fertige Texturen pro Sekunde. Die tatsächliche Rate hängt unter anderem von Timer, Submission, Backpressure, Queue-Latenz und Slot-Lifecycle ab. MaxSuccessfulSubmits zählt nur angenommene Submits; Busy- und Failed-Versuche verbrauchen das Limit nicht.
QueueProbe
Grundsyntax:
ExperimentalMultiGPU.StartQueueProbe <Hz> <MaxSubmits> [quiet] [Mode] [Queue]
| Modus | Ausgeführter Pfad |
|---|---|
full
|
Worker Compute → Cross-Adapter-Transfer → Primary Wait → Copy → Completion Fence. |
waitonly
|
Worker Compute → Cross-Adapter-Transfer → Primary wartet auf WorkerReadyFence → Completion Fence. Es findet keine Texturkopie statt. |
signalonly
|
Keine Textur, kein Worker-Wait und keine Kopie; nur Queue und Fence. Dieser Modus isoliert das Queue-Verhalten. |
| Queue | Bedeutung |
|---|---|
graphics
|
Vorhandene UE Primary Graphics-/Direct-Queue über RHIRunOnQueue.
|
copy
|
Vorhandene UE Primary Copy Queue über RHIRunOnQueue.
|
native
|
Vollständig private native D3D12-DIRECT-Queue auf der Primary. Derzeit nur für die Signal-only-Diagnose vorgesehen und kein fertiger Experimental-Modus. |
Typische Beispiele:
ExperimentalMultiGPU.StartQueueProbe 1000 100 quiet full graphics ExperimentalMultiGPU.StartQueueProbe 1000 100 quiet waitonly graphics ExperimentalMultiGPU.StartQueueProbe 1000 100 quiet signalonly graphics ExperimentalMultiGPU.StartQueueProbe 1000 100 quiet signalonly copy ExperimentalMultiGPU.StartQueueProbe 1000 100 quiet signalonly native
Der letzte Befehl ist aktuell die wichtigste isolierte Gegenprobe. Mit der privaten nativen Queue wurde bei 30 FPS eine durchschnittliche Latenz primaryExecuteToReadyAvgMs = 0.014 ms gemessen.
Benchmark-CVars
| CVar | Zweck |
|---|---|
r.VSync 0
|
Deaktiviert VSync, damit es die Messung nicht begrenzt. |
t.MaxFPS 30t.MaxFPS 60t.MaxFPS 120
|
Setzt die Engine-Framerate für reproduzierbare Frame-Coupling-Tests. |
t.MaxFPS 0
|
Entfernt das normale t.MaxFPS-Limit. Dieser Lauf ist als zusätzliche Diagnose-Gegenprobe vorgesehen.
|
Aktueller nächster Schritt
Der nächste Schritt ist, den QueueProbe-Lifecycle nach der bereits abgeschlossenen nativen Fence-Operation zu entkoppeln: Warum bleibt der Slot nach einer etwa 0,014 ms schnellen Fence-Completion noch durchschnittlich rund 32,4 ms bis zum nächsten erfolgreichen Submit belegt? Danach soll eine private native Queue WorkerReady-Wait und Cross-Adapter-Copy zunächst ausschließlich mit privaten Ressourcen testen. Erst auf dieser Grundlage wird entschieden, wie ein sicherer Experimental-Modus mit Capability-Selbsttest und automatischem Fallback auf den robusten Normal-Pfad aussehen kann.
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_UNORMmit Sample Count 1; weitere Formate, Mips, Arrays, Slices und MSAA sind offen. - Pro Worker existiert absichtlich nur ein persistenter Runtime-Slot. Mehrere parallele In-Flight-Jobs sowie Double-, Triple- oder Ring-Buffering fehlen noch.
- Die Primary-/SafeCopy-/Ready-Kette bleibt trotz zusätzlichem non-blocking
DispatchToRHIThreadeffektiv an den Frame-/Submission-Zyklus gekoppelt. - QueueProbe ist reine Diagnose. Der native Modus verwendet bisher nur QueueSignal und Fence, keine Textur oder Cross-Adapter-Ressource.
- Es gibt noch keine Umschaltung zwischen Normal und Experimental sowie keinen automatischen Session-Fallback.
- 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.
- Der sichtbare TPS-Nachweis zeigt eine Worker-Compute-Textur, noch keine auf dem Worker gerenderte 3D-Szene. Echte UE-Szenen-Workloads, Benchmark-Wizard und Scheduler fehlen weiterhin.
- Die projektseitigen UpscalerTest-Dateien mit DisplayActor und Texture Stream liegen außerhalb des Engine-Git-Repositories und sind nicht Bestandteil von Commit
43a5c07. - 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
- QueueProbe-Lifecycle entkoppeln und die verzögerte Slot-Freigabe nach schneller nativer Fence-Completion erklären.
- WorkerReady-Wait und Cross-Adapter-Copy auf einer privaten Primary-Ressource über die native Queue testen.
- Experimental-Architektur mit Capability-Selbsttest, Normal/Experimental-Einstellung und automatischem Session-Fallback definieren.
- Erst danach eine UE-sichtbare Experimental-Textur mit korrektem State-Tracking, Residency, Queue-Reihenfolge und Consumer-Synchronisation untersuchen.
- Nur bei nachgewiesenem Bedarf einen zweiten Slot oder Ringbuffer mit Generation-, Fence-, Reuse- und Consumer-Hazard-Modell ergänzen.
- Ersten echten UE-Offload wie SceneCapture2D, CCTV, Spiegel, Minimap, Niagara oder unabhängigen Compute integrieren.
- Formate, Mips, Arrays/Slices und gegebenenfalls MSAA capability-basiert erweitern.
- Capability-Matrix, Benchmark-Wizard, Scheduler und manuelle Overrides entwickeln.
- Mehrere Worker-GPUs unterstützen.
- Fallback-Matrix aus Normal Shared Buffer, System-RAM/Staging und einem späteren Experimental Fast Path ergänzen.
- Linux/Vulkan mit getrennten Devices, External Memory und externer Synchronisation untersuchen.
Quellen
- Interner Entwicklungsstand und verifizierte Testprotokolle vom 7. September 2026, Commit
43a5c07; vorheriger Checkpointcbf56c2 - Microsoft: Direct3D 12 Multi-adapter systems
- Microsoft: Shared heaps
- Microsoft: D3D12 Heterogeneous Multiadapter Sample
- Microsoft: UE4 Elemental Demo mit heterogenem Multiadapter
- Intel: Multi-Adapter Support in DirectX 12
- Microsoft: Ashes of the Singularity und heterogene Adapter
- Epic: NVIDIA SLI Alternate Frame Rendering
- Epic: Multi-Process Rendering
- Khronos: Vulkan Specification – Device Groups
