Mehr Performance: VALORANTs globale Invalidierung
Hallo!
Ich bin Aaron Cheney, Software Engineer im Leistungsteam von
VALORANT. Stabile Performance ist eine wichtige Komponente von
VALORANTs kompetitiver Integrität und unser Team überwacht, wartet
und verbessert sowohl die Leistung des Servers als auch die
Performance des Clients.
Heute
gibt es Neuigkeiten zu einer Funktion, an der wir mehrere Monate lang
gearbeitet haben: die
globale Invalidierung.
Bevor wir uns die Funktion im Detail ansehen, werfen wir zuerst
einmal einen Blick auf die Performance des Clients seit Patch 4.03.
Spoiler:
Da hat sich einiges getan.
Die
globale Invalidierung hatte für einen großen Teil unserer Spieler
deutliche Verbesserungen zur Folge. Es handelt sich hier um die
größte Leistungsverbesserung des Clients seit der Veröffentlichung
von VALORANT.
Auch
wenn diese Diagramme schon ziemlich gut aussehen – und uns die
Ergebnisse begeistern –, sollte man die Hintergründe
verstehen. Wir arbeiten mit großen, komplexen Datensätzen und um
das Spielererlebnis zu verstehen, organisieren, filtern und
kontrollieren wir sie. Um alle Zusammenhänge zu verstehen, musst du
Folgendes bedenken:
- Das Diagramm zeigt „Patch“ im Vergleich zur „durchschnittlichen Bildrate“; je höher die Zahlen, desto besser.
- Jede Linie repräsentiert eine häufig genutzte Hardwarekonfiguration (Prozessor und Grafikkarte) in unserer Spielerschaft. Bei der Analyse von Leistungsdaten betrachten wir diese Konfigurationen als wichtigsten Faktor für die erwartete Performance. In diesen Diagrammen sind Rechner mit derselben Prozessor- und Grafikkartenkonfiguration zusammengefasst.
- Die Datenproben stammen aus zwei Warteschlangen: ungewertet und gewertet. Da diese beiden Spielmodi am beliebtesten sind, investieren wir viel Zeit und Energie, um die Performance in diesen Bereichen zu verstehen und zu verbessern.
- Matches, an denen nicht genau 10 Spieler teilnehmen, werden nicht gezählt. So verhindern wir, dass Ausreißer die Daten verzerren. (Matches mit weniger Teilnehmenden haben auch eine bessere Leistung.)
ZUSAMMENFASSUNG DER GLOBALEN INVALIDIERUNG
Die
globale Invalidierung sorgt für bis zu 15 %
mehr Leistung bei Clients mit limitierendem Prozessor (allgemein
Rechner der Mittel- bis High-End-Klasse). Diese Steigerungen sind das
Ergebnis von monatelanger Arbeit in mehreren Teams. Unser Prozess zur
Identifizierung optimierungsfähiger Bereiche im Spiel hat sich
bewährt und das Risikomanagement währenddessen für ein stabiles
Spielererlebnis gesorgt.
Wer davon profitiert, und realistische Erwartungen
Laut
unserer Live-Messungen sorgt die globale
Invalidierung für bis zu 15 %
mehr Leistung bei Client-Konfigurationen mit limitierendem Prozessor
(allgemein Rechner der Mittel- bis High-End-Klasse).
Der
deutliche Aufwärtstrend in der Gesamtbetrachtung repräsentiert
allerdings nicht das Spielgeschehen im Detail. Außerdem ist nicht
gewährleistet, dass alle Rechner mit entsprechender Hardware auch
von denselben Ergebnissen profitieren.
Das
bedeutet, dass sich die grundlegende Leistung von VALORANT auf
Rechnern mit limitierendem Prozessor allgemein verbessert hat.
Allerdings hängt die Gesamtleistung deines Rechners von
verschiedenen individuellen Faktoren ab.
DIE GLOBALE INVALIDIERUNG VERSTEHEN
Bevor
wir uns einen Überblick über die globale Invalidierung verschaffen,
müssen wir zuerst über Elemente der Benutzeroberfläche in der
Unreal Engine sprechen.
Widgets und Baumstrukturen
Die
Elemente der Benutzeroberfläche (auch bekannt als Widgets) werden
mithilfe einer Baumstruktur aus kleineren Bausteinen zusammengesetzt.
Die Baumstruktur entspricht dem Dateisystem auf deinem Computer. Ein
Widget kann eine beliebige Anzahl an Kindern haben (wie auch ein
Ordner eine beliebige Anzahl an Dateien enthalten kann).
Diese Bausteine kann man zu komplexen Widgets kombinieren. Unsere Munitionsanzeige besteht beispielsweise aus vielen Teilen und der Baum sieht folgendermaßen aus:
Zusammengenommen sehen die Bausteine für die Munitionsanzeige so aus:
(Die roten Umrisse sind hervorgehoben, um sie deutlich zu machen. In der Praxis würden sie sich überschneiden.)
Wenn
sich ein oder mehrere Widgets in der Baumstruktur ändern, kann das
mehrere andere Widgets beeinflussen. Wenn zum Beispiel ein Widget auf
dem Bildschirm eine neue Position einnimmt, müssen alle
untergeordneten Widgets auch eine neue Position berechnen. Diese
Änderungen verwaltet ein System namens „Invalidierung“. Im
nächsten Abschnitt sehen wir es uns genauer an.
Invalidierung
Invalidierung
ist ein Mechanismus in der Unreal Engine, der meldet, wenn ein Widget
sich verändert hat und aktualisiert werden muss.
Ein
Widget muss aus verschiedenen Gründen invalidiert werden:
Animationen, Farbe, Transparenz, Größe, Anordnung, Text, Bilder und
viele weitere Eigenschaften können sich als Reaktion auf das
Spielgeschehen ändern. Sobald diese Änderungen eintreten, wird das
Widget „invalidiert“, es verliert also seine Gültigkeit und muss
aktualisiert werden.
Allerdings
ist dieser Prozess etwas komplizierter, da es verschiedene Arten von
Invalidierung geben kann. Dazu gehören:
- Anordnung – Wenn sich die Größe des Widgets ändert (sehr ressourcenintensiv).
- Farbe – Wenn sich das Aussehen des Widgets, aber nicht die Größe geändert hat.
- Anordnung der Kinder – Wenn sich die Anordnung der Widgets innerhalb eines Baumes geändert hat (betrifft auch die Anordnung und ist daher ressourcenintensiv).
- Sichtbarkeit – Wenn sich die Sichtbarkeit eines Widgets ändert, es also entweder unsichtbar oder sichtbar wird (betrifft auch die Anordnung und ist daher ressourcenintensiv).
Diese
Arten der Invalidierung geben an, welche Operationen zur korrekten
Anzeige ausgeführt werden müssen.
Das
Ganze wird noch komplizierter, wenn ein Widget von einem anderen
abhängig ist. Widgets sind in Hierarchien organisiert und die
Anordnung hängt von verschiedenen Faktoren ab. Wenn ein einzelnes
Widget invalidiert wird, müssen möglicherweise auch mehrere
abhängige Widgets invalidiert werden, bevor es neu angezeigt werden
kann. Wenn beispielsweise mehrere Widgets in einer vertikalen
Anordnung organisiert sind (wie im Fenster „Sozial“ mit der
Freundesliste) und sich die Reihenfolge der Widgets ändert (wenn ein
Freund online kommt), müssen alle Widgets innerhalb der vertikalen
Anordnung aktualisiert werden.
In
so einem System gibt es mehrere Vorgaben:
- So wenig Widgets wie möglich invalidieren. Das verringert die Anzahl der Widgets, die aktualisiert werden müssen, bevor ein Element angezeigt werden kann.
- Widgets nur wenn nötig invalidieren. Wird ein Widget unnötigerweise invalidiert, verschwendet man wertvolle Prozessorzyklen.
- Das Ergebnis zwischenspeichern, wenn ein Widget nicht invalidiert wird, um es bei jedem Einzelbild schnell anzeigen zu können. Wenn sich nichts ändert, lassen sich Prozessorzyklen sparen.
Wir
sollten jetzt eine Ahnung davon haben, wie Widgets aktualisiert
werden und was eine Invalidierung auslösen kann. Als Nächstes sehen
wir uns an, wie Entwickler die Theorie in der Praxis umsetzen.
Invalidierungsboxen
Die
Unreal Engine stellt eine Komponente bereit – nämlich
Invalidierungsboxen –, mit der sich mehrere Widgets gruppieren
lassen. Widgets in einer Invalidierungsbox werden nicht vorgerendert,
dargestellt und es wird kein Code für sie ausgeführt. Stattdessen
werden die Ergebnisse in einem Vertex-Puffer zwischengespeichert.
Sobald
ein Widget in der Invalidierungsbox invalidiert wird, werden
zwischengespeicherte Daten gelöscht und die Widgets werden
aktualisiert und neu dargestellt. Die Aktualisierung des
Zwischenspeichers für ein Einzelbild ist zwar ressourcenintensiv,
die Ergebnisse machen sich langfristig allerdings bezahlt.
Invalidierungsboxen
waren extrem wichtig für die Leistung der Benutzeroberfläche in
VALORANT, besonders kurz vor der Veröffentlichung.
Aber sie sind nicht umsonst:
- Entwickler müssen wissen, welche Widgets sich gut in einer Invalidierungsbox gruppieren lassen. Widgets, die sich regelmäßig aktualisieren, eignen sich nicht dafür.
- Entwickler müssen Widgets manuell in einer Invalidierungsbox platzieren. Dieser Aufwand lohnt sich nicht für jedes Widget im Spiel, deshalb muss man wissen, welche Widgets sinnvoll in einer Invalidierungsbox aufgehoben sind.
Weitere
Informationen zu Invalidierungsboxen findest du in der Dokumentation
von Epic (in englischer Sprache).
Jetzt
kennen wir den nötigen Kontext, um uns über die globale
Invalidierung zu unterhalten.
Die globale Invalidierung betritt die Bühne
Bestimmt
fragst du dich jetzt, warum man nicht einfach alle Elemente der
Benutzeroberfläche in eine globale Invalidierungsbox packt. Und
genau das tut die globale Invalidierung auch (zumindest so etwas in
der Art).
Die
globale Invalidierung soll die Leistung der Benutzeroberfläche im
Spiel deutlich verbessern und gleichzeitig die nötige manuelle
Arbeit von Entwicklern verringern, die Widgets in individuellen
Invalidierungsboxen platzieren müssen.
Problem gelöst!
Allerdings
unterstützt die Unreal Engine 4.25 (die Version, die VALORANT
verwendet) die globale Invalidierung nicht universell für alle
Widget-Typen. Spätere Versionen der Engine haben Verbesserungen
implementiert, doch die konnte VALORANT nicht sofort nutzen. Außerdem
hatten wir nicht auf dem Schirm, wie deutlich
die globale Invalidierung die Leistung von VALORANT beeinflussen
würde.
Und
hier ging unsere Arbeit los.
WARUM HABEN WIR DIESE ARBEIT AUF UNS GENOMMEN?
Ende
Juli 2021 beschloss das Team, sich die globale Invalidierung in einem
internen Testlauf anzusehen. Es wurden kleinere Änderungen
vorgenommen, um ein paar Fehler zu beheben und den Testlauf
erfolgreich durchzuführen. Allerdings war uns klar, dass es während
des Testlaufs zu Fehlern kommen würde … und da lagen wir auch
richtig.
Nach
dem Testlauf hatten wir um die 20 Fehler gefunden – und
das waren nur die offensichtlichen. Es warteten wahrscheinlich auch
noch unauffällige, hinterhältige Fehler darauf, entdeckt zu werden.
Ganz zu schweigen von mehreren Grenzfällen, die wir nicht gesondert
getestet hatten.
Aber …
sorgte die globale Invalidierung für eine verbesserte Leistung?
Absolut.
Die
Datenanalyse nach diesem Testlauf ergab, dass die Benutzeroberfläche
jetzt ~35 % schneller lief.
(Hinweis: Die Benutzeroberfläche ist nur ein Teil der Kosten für
ein Einzelbild.)
Allerdings
waren noch viele Fragen offen:
- Wie lange würde es dauern, alle Fehler zu beheben?
- Welche Teams sollten sich darum kümmern?
- Sollten wir diese Arbeit vor allen anderen Vorhaben priorisieren? Um regelmäßig neue Inhalte abliefern zu können, sind unsere Kalender oft schon Monate im Voraus vollgeplant. Unerwartete Projekte, so interessant sie auch sein mögen, kriegt man da nur mit Mühe unter.
- Werden die Leistungsvorteile weniger, wenn wir die Fehler beheben? Fehler lassen sich nur mit Änderungen am Code beheben, welche die Benutzeroberfläche potenziell wieder ressourcenintensiver machen können.
- Wann haben wir Zeit dafür? Da wir wussten, dass die globale Invalidierung in späteren Versionen der Unreal Engine aktiv entwickelt wurde, mussten wir unseren eigenen Zeitplan abstimmen, um die Änderungen von Epic zu implementieren.
Letzten
Endes sahen wir den Aufwand aus mehreren Gründen als lohnenswert an.
Implementierung in die Unreal Engine und Überlegungen hinsichtlich des Zeitplans
Unreal
Engine 4.26 und 4.27 bieten zwar deutliche Fortschritte im Bereich
der globalen Invalidierung, VALORANT implementiert neue Versionen
allerdings mit Verzögerung. Wir sind nicht immer am „Puls der
Zeit“, um Risiken zu vermeiden und unseren Spielern Stabilität zu
garantieren.
Laut
des damaligen Zeitplans wollten wir noch viele weitere Monate mit der
Unreal Engine 4.25 arbeiten, sodass die Spieler erst in über einem
Jahr von diesen Leistungsoptimierungen profitiert hätten. Das
konnten wir nicht verantworten.
Weitere
Details über die Philosophie hinter Unreal Engine-Upgrades für
VALORANT findest du in diesem
Twitter-Beitrag
von VALORANTs Tech Lead Marcus Reid (in englischer Sprache).
Messbare Leistungssteigerungen
Die
globale Invalidierung war in Sachen Leistungsoptimierung ziemlich
einmalig, da sich ihr Wert
messen
ließ. Wir konnten die mögliche Leistungssteigerung in einem
internen Testlauf feststellen und danach war unsere Strategie
(größtenteils) klar.
Leistungsoptimierungen
sind schwierig. Normalerweise verbessern kleine Änderungen die
Leistung im Lauf der Zeit und eine einzige Änderung bietet nur in
äußerst seltenen Fällen Leistungsgewinne im zweistelligen Bereich.
Wir konnten diese Gelegenheit also unmöglich ungenutzt lassen.
Auch
nach Abzug der Leistungsverluste aufgrund von Fehlerbehebungen
konnten wir den Spielern mithilfe der globalen Invalidierung
innerhalb eines angemessenen Zeitraums potenziell deutliche
Verbesserungen bieten.
WIE HABEN WIR DAS ANGESTELLT?
Obwohl
wir bereits Ende Juli 2021 die ersten Experimente mit der globalen
Invalidierung durchgeführt hatten, begannen die wirklichen Arbeiten
erst Ende September 2021.
Änderungen von Epic selektiv implementieren
Eine
komplette Implementierung von Unreal Engine 4.26 und 4.27 stand zwar
außer Frage, doch Epic hatte bereits aktiv an der globalen
Invalidierung gearbeitet. Wir sahen uns also Tausende von Änderungen
an und entschieden, welche wir für eine stabile, funktionstüchtige
globale Invalidierung in unserer Version der Engine gebrauchen
konnten.
Änderungen
teilweise zu implementieren war eine echte Herausforderung. Wir
wollten so wenige Kernfunktionen der Engine wie möglich
modifizieren, um die Stabilität nicht zu beeinträchtigen, aber
gleichzeitig die richtigen Änderungen von Epic einbauen. Die
gesamten Arbeiten fanden isoliert von VALORANTs Hauptversion statt,
um die anderen Entwickler nicht zu stören.
Nachdem
wir die Änderungen von Epic selektiv in unsere Engine übernommen
hatten, behoben wir im Laufe von mehreren Wochen so viele Fehler wie
möglich. Gleichzeitig bereiteten wir die Einführung der globalen
Invalidierung in die Hauptversion von VALORANT vor. Parallel dazu
implementierten wir eine
Schaltfläche, über die sich die Funktion schnell aktivieren und
wieder ausschalten ließ
(für den Fall, dass es zu einer Katastrophe kam).
Nachdem
viele Fehler aus dem Weg geräumt und viele von Epics Änderungen für
die Unreal Engine 4.26 und 4.27 eingebaut waren, fusionierten wir
unsere isolierte Version wieder mit der Hauptversion.
Gemeinsamkeiten zwischen Fehlern finden
Obwohl
viele der Fehler im Zusammenhang mit der globalen Invalidierung sich
auf unterschiedliche Arten äußerten, war die Wurzel allen Übels
oft ein einziges Problem. Diese Fehler hatten für uns oberste
Priorität, da wir so mehrere Probleme mit einer einzigen Änderung
beseitigen konnten. Eine Anpassung konnte zum Beispiel über
10 Fehler berichtigen, die über das Spiel verteilt waren. Die
gründliche Analyse des grundlegenden Problems führte zu robusten
Lösungen, die sowohl die Verlässlichkeit als auch die Stabilität
der globalen Invalidierung verbesserten.
Fehler identifizieren, beheben, testen – und wieder von vorne
Die
nächsten Wochen und Monate spielten sich nach folgendem Muster ab:
Globale Invalidierung vor einem Testlauf aktivieren, eine Reihe
Fehler finden, globale Invalidierung nach dem Testlauf deaktivieren
und die Fehler beheben.
Mit
jedem neuen Durchgang fanden wir weniger Fehler. Wir machten weiter,
bis der ständige Strom an Fehlern schließlich nachließ und dann
ganz versiegte.
Gegen
Ende November 2021 hatten wir alle großen Probleme beseitigt und die
globale Invalidierung war größtenteils stabil.
Nennenswerte Fehler
- Astra lässt das Spiel abstürzen – Vorübergehend mussten sich Astra-Spieler mit Abstürzen herumschlagen. Dieser Fehler ließ sich nach der Implementierung der Änderungen von Epic beheben.
- Absturz bei Mehrfachvererbung – Mehrfachvererbung in C++ ist ein kniffliges Thema. Ohne zu tief in die Materie einzusteigen: Die Destruktoren einer Klasse wurden nicht in der richtigen Reihenfolge ausgeführt, was zu einem Absturz führte. Wir mussten lediglich zwei Zeilen Code vertauschen, um die Vererbungsreihenfolge zu ändern und das Problem zu lösen. Weitere Informationen zu Mehrfachvererbung findest du auf dieser Seite (in englischer Sprache).
- Pausenlos wiederholtes Audio der Chatleiste – Wenn man im Menü mit dem Mauszeiger über die Chatleiste fährt, erklingt ein Ton. Aufgrund eines Fehlers wurde dieses Audio mehrmals pro Sekunde abgespielt, was natürlich allen auf die Nerven ging. Um das Problem zu beheben, mussten wir erst verstehen, wie Widgets mehrere Male pro Einzelbild Mausereignisse empfangen und wie das zugehörige Timing funktioniert.
Methoden beim Testen
Globale
Invalidierung wirkt
sich auf alle Aspekte des Spiels aus.
Ohne Ausnahme. Buchstäblich.
Deine
Freundesliste? Globale Invalidierung. Die Schaltfläche, die dich in
die Warteschlange wirft? Globale Invalidierung. Das Einstellungsmenü?
Dreimal darfst du raten. Meine Kopfschussrate? Ähm …
Es
gibt überall im Spiel Elemente der Benutzeroberfläche und sie
teilen Spielern wichtige Informationen mit. Diese
Elemente durften unter keinen Umständen kaputtgehen.
Also
stellte unsere Qualitätssicherung einen Testplan mit mehreren
Strategien zusammen, um mit Zuversicht sagen zu können, dass die
globale Invalidierung auch wie gewünscht funktionierte.
Tests im „Vertical Slice“
Ein
„Vertical Slice“ ist in der Spieleentwicklung eigentlich eine
kurze Demo, die einen Eindruck vom Spiel vermitteln soll. In VALORANT
stellt es die Hauptinteraktionen der Spieler dar, vom Start des
Clients bis hin zur Warteschlange, dem kompletten Match und dem
Spielergebnis-Bildschirm. So konnte sich die Qualitätssicherung auf
die kritischen Elemente des Spiels konzentrieren, die am häufigsten
verwendeten Elemente schnell testen und Probleme schon früh
identifizieren.
Zerstörerische Tests
Nach
dem „Vertical Slice“ ging das zerstörerische Testen an den
Start. Diese Art von Tests dient der Ermittlung von Problemen, die
von der Norm abweichen, häufig durch Änderung externer Faktoren
(z. B. Netzwerk-Ping, Bildrate, Wechsel zwischen Programmen mit
ALT und TAB usw.). Mithilfe einer Reihe interner Werkzeuge
verbrachten die Qualitätssicherer mehrere Wochen mit zerstörerischen
Tests.
Grenzfall-Tests
Viele Teile von VALORANT bekommt nur ein kleiner Prozentteil der Spielerschaft zu Gesicht. Einige Bereiche sieht man nur ein einziges Mal (z. B. die Einführung für neue Spieler). Auch wenn diese Elemente nicht so oft aufgerufen werden, sind sie trotzdem wichtig. Um auch versteckte Fehler zu finden, mussten wir also unsere Grenzfälle identifizieren und testen.
Tests auf der PBE (Öffentliche Beta-Testumgebung)
Die
PBE war für die globale Invalidierung ein großer Meilenstein.
- Zum ersten Mal konnten externe Spieler die Funktion ausprobieren. Das hieß auch, dass die globale Invalidierung unter den Bedingungen der „echten Welt“ getestet wurde.
- Auf der PBE gibt es viele verschiedene Hardwarekonfigurationen. Die PBE deckt bewusst ein ganzes Spektrum an Rechnern ab, von Einsteiger bis High End, und dank der unterschiedlichen Endgeräte konnten wir sehen, wie die globale Invalidierung sich auf die Spielerschaft auswirken würde.
Nach
dem PBE-Test am Wochenende des 22.–23. Januar 2022 konnten wir
mit Zuversicht sagen, dass die globale Invalidierung die kompetitive
Integrität nicht negativ beeinflusste und die
Leistungsverbesserungen unseren Erwartungen entsprachen.
Globale Invalidierung für alle
Als
die globale Invalidierung mit Patch 4.03 live ging, behielten wir die
Fehlermeldungen der Spieler genau im Auge. Außerdem überwachten wir
die Leistungsdaten, um sicherzustellen, dass sich unsere Schätzungen
mit den realen Ergebnissen deckten. Letzten Endes war die globale
Invalidierung ein großer Erfolg für unsere Spieler, und wir hoffen,
dass du von der verbesserten Bildrate profitierst.
Da
die globale Invalidierung jetzt im Live-Spiel ist, macht sich das
Leistungsteam wieder daran, weiter zu optimieren. Bis zum nächsten
Mal und viel Spaß beim Fraggen!