Hier ist James Fulop, Senior Engine Programmer im Guild Wars 2 Team. Bei der DirectX11-Unterstützung handelt es sich um ein Projekt, an dem schon lange gearbeitet wird. In diesem Beitrag gehe ich auf einige der wichtigsten technischen Entscheidungen ein, die getroffen wurden, um die Form dieses Projekts zu bestimmen. Außerdem gebe ich euch einen Überblick über unsere Grafik-Runtime und die Auswirkungen des DirectX11-Renderers auf die Leistung.
Die Beta startet am 21. September 2021. Ihr könnt euch Spiel im Menü „Grafikoptionen“ für die Beta anmelden. Die Änderung wird nach dem Neustart des Spiels wirksam.
Warum haben wir uns überhaupt für ein Upgrade auf DirectX11 entschieden? Die Client-Leistung hat für uns Priorität, und wir möchten, dass jeder mit der höchstmöglichen Bildwiederholrate spielen kann. Wir haben festgestellt, dass das Spiel manchmal ins Stocken gerät, während das Rendering abgeschlossen wird. Guild Wars 2 gibt es nun schon seit neun Jahren und die Implementierung einiger DirectX11-abhängiger Features kann dazu beitragen, dass das Spiel auch in Zukunft schön aussieht.
DirectX11 bietet außerdem einige moderne technologische Möglichkeiten, die in DirectX9 nicht verfügbar sind. Das Upgrade auf DirectX11 ist der erste Schritt, um mehr tolle Dinge verwirklichen zu können.
Nach sorgfältiger Recherche haben wir uns entschieden, die Open-Source-Rendering-Bibliothek BGFX in Guild Wars 2 zu integrieren. BGFX ist gut programmiert, unterstützt verschiedene Grafik-Backends und wird bereits in zahlreichen Spielen in der Branche eingesetzt. Auf der offiziellen Webseite von BGFX erhaltet ihr weitere Informationen.
Da sich die Grafik-Ökosysteme in der Computerindustrie ständig weiterentwickeln, können wir mit BGFX als Gemeinschaft von Rendering-Technikern zusammenarbeiten, anstatt dass in jedem Studio das Rad neu erfunden werden muss. ArenaNet hat und wird weiterhin zur Entwicklung von BGFX beitragen.
Wir haben uns für DirectX11 anstelle von DirectX12 oder Vulkan entschieden, da wir festgestellt haben, dass die Umstellung auf die DirectX11-Implementierung von BGFX eine so große Leistungsverbesserung brachte, dass das Grafik-Backend nicht länger ein einschränkender Faktor für die Client-Leistung war. DirectX11 ist sehr stabil und wird bereits seit fast einem Jahrzehnt von Tausenden von Spielen verwendet. Mit DirectX11 können wir Systeme bis zurück zu Windows Vista unterstützen. Vulkan unterstützt hingegen erst Systeme ab Windows 7 und DirectX12 erst Systeme ab Windows 10. In puncto Grafik eröffnet uns der Sprung von DirectX9 zu DirectX11 in den kommenden Jahren viele Möglichkeiten zur Erweiterung der Engine um interessante Funktionen. Die Unterstützung von mehr als einem dieser Backends würde eine riesige Menge Arbeit für die Qualitätssicherung bedeuten und nur wenige handfeste Vorteile bieten.
Am aktuellen DirectX9-Renderer wurden keine nennenswerten Änderungen vorgenommen. Eine der Philosophien, die ich bei der Entwicklung verfolgte, war, dass der DirectX11-Renderer genauso aussehen sollte wie DirectX9. Wenn ich an beiden gleichzeitig Änderungen vornehmen würde, gäbe es keine Grundlage dafür, wie die Dinge aussehen sollen. Irgendwann wird der DirectX9-Renderer veraltet sein und entfernt werden, sobald der neue Renderer stabil läuft.
Über Frames
Jetzt möchte ich erläutern, wie der DirectX11-Renderer die Leistung des Spiels beeinflusst. Zunächst einmal funktionieren Videospiele genau wie ein Film: Man sieht Standbilder, die schnell gewechselt werden. Diese einzelnen Standbilder nennen wir Frames. Von diesem Prozess stammt der Begriff „Bilder pro Sekunde“ (auf Englisch: Frames per Second, kurz: FPS). Je höher die FPS, also die Bildwiederholrate, desto flüssiger sieht das Geschehen aus, bis die physikalische Grenze eures Anzeigegeräts erreicht ist. Dieser Vorgang, bei dem man ständig auf Eingaben (Tastatur und Maus) reagiert und Bilder produziert, ist die Spielschleife.
Es folgen einige Zeitleisten-Visualisierungen der Spielschleife. Die Daten von der oben genannten Stelle in Löwenstein wurden unter Verwendung der Grafikeinstellung „Beste Qualität“ gewonnen.
Ich habe diese Stelle gewählt, weil es hier viel zu rendern gibt. Da ist die ganze Stadt, und die Technik, die wir für die Echtzeit-Reflexionen verwenden, ist im Grunde ein zweites Rendering der Welt, nur gespiegelt.
Für diesen Test verwende ich eine Intel I7-6700 CPU und eine Nvidia 1080 GPU.
Oben seht ihr einen Screenshot eines von uns verwendeten Visualisierungstools namens Telemetry, das von RAD Game Tools entwickelt wurde. Er zeigt, wie die Arbeit des Spiels auf die Kerne eurer CPU verteilt wird. Die Zeit wird waagerecht dargestellt. Hier werden zwei Frames der Spiellogik angezeigt. Für eine bessere Übersichtlichkeit habe ich viele der Bezeichnungen unscharf gemacht.
Jede horizontale Reihe steht für einen logischen Thread. Hier könnt ihr sehen, dass ich sechs Arbeitsthreads habe. Wir passen die Anzahl der Arbeitsthreads an, je nachdem, wie viele CPU-Threads eure CPU unterstützt. Ich habe einen Chip, der acht Hardware-Threads unterstützt, also teilt das Spiel sie in einen Spielthread, einen Renderthread und sechs Arbeitsthreads auf. Bei Guild Wars 2 laufen auch andere Threads, die ich hier nicht zeige. Diese Threads sind nicht besonders rechenintensiv und für diesen Beitrag nicht relevant.
Die verschiedenen Blöcke innerhalb der Threadkanäle stellen die Arbeit dar. Wenn der Thread als leer angezeigt wird, bedeutet dies, dass er sich im Leerlauf befindet und keine Arbeit für das Spiel verrichtet. In diesen Bereichen kann der Hardware-Thread Arbeit von anderen Anwendungen auf eurem Rechner übernehmen.
Logische Frames sind die Stellen, an denen wir die Spielschleife als neu gestartet betrachten. Vertikale Linien markieren den Anfang logischer Frames. Hardware-Eingaben (wie Tastatur/Maus) werden hier zu Beginn abgefragt.
Hier wird aus dem Grafik-Frame eine Schleife. Da am Anfang des Spiel-Frames kein Rendering stattfindet, lassen wir einen Teil der Arbeit des letzten Frames in den nächsten Frame überlaufen, damit wir so viel Arbeit wie möglich parallel erledigen können. Irgendwann muss man jedoch warten, bis der letzte Frame fertig ist, damit man die Arbeit für den nächsten Frame abschicken kann. Hier ist euch vielleicht ein roter Arbeitsblock im Hauptthread aufgefallen, der sich mit einem Teil der Arbeit auf dem Renderthread deckt. Genau das wollen wir beseitigen. Der Spielthread sollte nie auf den Abschluss der Rendering-Arbeiten warten müssen.
Nebenbei bemerkt: Es gibt noch einige andere, viel kleinere rote Blöcke im Hauptthread. Das ist der Hauptthread, der auf den Abschluss der Arbeit des Auftragthreads wartet.
Hier erstellen wir detaillierte Listen von Anweisungen, die der Renderthread ausführen soll. Wenn ein Block allgemeiner Rendering-Arbeiten generiert wurde, wird er an den Renderthread weitergegeben.
Der Renderthread wandelt die Rendering-Arbeit gemäß der Besonderheiten des jeweiligen Grafik-Backends (OpenGL oder DX9) um.
DX11-Beta-Rendering-Frame
So sieht der neue Renderer im Profiler aus.
Die Anordnung der Threads ist die gleiche.
Der Hauptthread muss nicht mehr warten, bis die Arbeit des Renderthreads abgeschlossen ist. Das ist aufgrund der Architektur von BGFX möglich. Zeichnen-Aufrufe werden im Spielthread gesammelt. (Und auch in den Arbeitsthreads, dazu kommen wir noch). Wenn der Frame dann endet, verarbeitet BGFX diese Befehle in seinem Renderthread. Jetzt haben wir eine Menge Spielraum zum Zeichnen!
Die Umwandlung der Rendering-Daten aus der Warteschlange in BGFX-Befehle erfolgt parallel in verschiedenen Auftragthreads.
Das war’s! Für mich war dieses Projekt sehr interessant. Ich bin gespannt auf die technische Zukunft von Guild Wars 2.
Bitte lasst uns in den Foren wissen, wie die DirectX11-Beta bei euch funktioniert.
Wir sehen uns in Tyria!
James Fulop