Amélioration technique : Global Invalidation dans VALORANT
Bonjour !
Je suis Aaron Cheney, ingénieur logiciel dans l'équipe de VALORANT
dédiée à la performance technique. Au sein de cette pierre
angulaire de l'intégrité compétitive du jeu, notre équipe est
responsable de la surveillance, de la maintenance et de
l'amélioration des performances des serveurs et du client.
Aujourd'hui,
nous sommes heureux de pouvoir vous donner des nouvelles d'une
fonctionnalité que nous avons passé plusieurs mois à développer :
la
Global Invalidation.
Nous allons vous expliquer ce que c'est exactement dans un moment,
mais commençons par prendre le temps de visualiser quel a été son
impact sur la performance du client depuis le patch 4.03.
On
vous le dit tout de suite : c'est assez énorme.
Une
proportion importante de nos joueurs a bénéficié d'une
amélioration significative grâce à la mise en place de la Global
Invalidation. Mieux : c'est
le plus gros gain de performance de notre client depuis le lancement.
Si
ces courbes font plaisir à voir (et nous sommes ravis du résultat),
il faut comprendre ce qu'elles signifient exactement. Nous
travaillons avec des ensembles de données énormes et complexes. Les
organiser, les filtrer et les contrôler nous aide à mieux
comprendre l'expérience des joueurs. Voici ce que vous devez garder
en tête pour saisir la question dans sa globalité :
- Le graphique mesure les « IPS du patch » par rapport aux « IPS moyennes » : plus c'est haut, mieux c'est.
- Chaque courbe représente une configuration matérielle (combinaison de processeur + carte graphique) fréquente chez nos joueurs. Quand on analyse les données de performance, on considère que c'est la combinaison qui prédit le mieux les performances attendues. Les appareils équipés de la même combinaison de processeur + carte graphique sont regroupés dans ces graphiques.
- Nous prélevons des données dans deux modes de jeu : en non-classé et en Compétition. Ce sont les plus populaires et nous avons investi beaucoup d'efforts dans la compréhension et l'amélioration de ces domaines.
- Nous avons écarté les parties qui ne comptaient pas exactement 10 joueurs. Cela évite que les cas marginaux faussent les données (les parties avec moins de joueurs ont de meilleures performances).
RÉSUMÉ DE LA GLOBAL INVALIDATION
La
Global Invalidation offre un gain de 15%
aux clients dépendants des processeurs (soit généralement les
appareils de moyenne à haute gamme). Parvenir à ce résultat a
mobilisé plusieurs équipes et demandé plusieurs mois de travail.
La procédure que nous avons mise en place pour identifier les
aspects du jeu mûrs pour l'optimisation a payé, et contrôler les
risques tout du long a garanti que votre expérience du jeu ne soit
pas perturbée.
Les gagnants et le calibrage des attentes
Selon
nos mesures en temps réel, la
Global Invalidation a permis une amélioration allant jusqu'à 15%
pour les configurations de clients qui reposent sur le processeur
(soit
généralement les appareils de moyenne à haute gamme).
Si
la tendance à la hausse est remarquable dans le total, cela ne
représente pas ce qui se passe en jeu en temps réel. Cela ne
garantit pas non plus que tous les appareils équipés de matériel
similaire obtiennent les mêmes résultats.
En
d'autres termes, la performance de base de VALORANT sur les machines
dépendantes de leur processeur a globalement augmenté, mais la
performance individuelle de votre machine à vous dépend d'autres
facteurs qui vous sont propres.
COMPRENDRE CE QU'EST LA GLOBAL INVALIDATION
Avant
de vous montrer ce qu'est la Global Invalidation, nous devons d'abord
clarifier certains éléments de l'IU du moteur Unreal Engine.
Widgets et arborescences
Les
éléments d'IU (qu'on appelle aussi « widgets ») sont
créés à partir de petits blocs de construction selon une
arborescence, qui ressemble à la façon dont vos fichiers sont
rangés dans votre ordinateur. Un widget peut avoir un certain nombre
d'enfants (de la même façon qu'un dossier peut contenir plusieurs
fichiers).
Ces blocs de construction peuvent se combiner pour former des widgets complexes. Notre compteur de munitions, par exemple, est constitué de plusieurs parties, selon une arborescence qui ressemble un peu à ça :
Quand on assemble le tout, le bloc de construction dédié au compteur de munitions ressemble à ça :
(Les
boîtes rouges sur l'image ci-dessus sont affichées pour que ce soit
plus clair. En pratique, elles se chevauchent souvent.)
Dès
qu'un ou plusieurs widgets de la structure sont modifiés, cela en
affecte beaucoup d'autres. Par exemple, si un widget change de
position à l'écran, tous ceux qui étaient placés en dessous
doivent recalculer leur position. Ce sont ces conséquences que gère
le système qu'on appelle « Invalidation » et dont nous
allons parler dans la partie suivante.
Invalidation
L'invalidation
est le mécanisme dont se sert Unreal Engine pour indiquer qu'un
widget en particulier vient d'être modifié et doit être mis à
jour.
De
nombreuses raisons justifient l'invalidation d'un widget :
l'animation, la couleur, l'opacité, la taille, l'ordre, le texte,
les images et bien d'autres propriétés peuvent changer en réponse
à un événement en jeu. Quand un widget subit l'un de ces
changements, il est « invalidé » et signale ainsi qu'il
doit être mis à jour.
Pour
rajouter une couche de complexité, un widget peut avoir plusieurs
types d'invalidation. Parmi lesquels :
- Agencement : quand la taille d'un widget a changé (très coûteux).
- Revêtement : quand l'apparence d'un widget a changé, mais pas sa taille.
- Ordre des enfants : quand l'ordre des widgets a changé à l'intérieur d'une arborescence (ce qui concerne aussi l'agencement, donc très coûteux).
- Visibilité : quand la visibilité d'un widget a changé, c'est-à-dire qu'il est devenu visible ou invisible (ce qui concerne aussi l'agencement, donc très coûteux).
Ces
types d'invalidation servent à indiquer quel genre d'opération doit
être effectuée pour que cela fonctionne.
Ça
devient encore plus compliqué quand un widget dépend d'un autre. En
effet, ils sont organisés selon une hiérarchie et leur agencement
dépend d'un certain nombre de facteurs. Invalider un seul widget
peut demander d'en invalider beaucoup d'autres liés pour que tout
fonctionne. Prenons l'exemple de widgets rangés verticalement (comme
votre liste de d'amis), dont l'ordre change (parce qu'un de vos amis
se connecte), alors tous ceux à l'intérieur de cette boîte
verticale doivent être mis à jour.
Un
système de ce genre souhaite tendre vers plusieurs objectifs :
- Invalider le moins de widgets possible. Cela réduit le nombre de widgets qui devront être mis à jour au final pour que le code fonctionne.
- N'invalider un widget que lorsque c'est strictement nécessaire. Invalider un widget pour rien gâche de précieux cycles de processeur.
- Quand un widget n'est pas invalidé, mettre le résultat en cache pour le répéter rapidement à toutes les images. Si rien de change, cela permet d'économiser des cycles de processeur.
Vous
avez à présent les mains suffisamment dans le cambouis pour
comprendre comment les widgets se mettent à jour et quel genre
d'événement provoque une invalidation. Voyons maintenant comment
les développeurs mettent cela en pratique.
Boîtes d'invalidation
Unreal
Engine met à disposition un élément qu'on appelle « boîte
d'invalidation » pour regrouper plusieurs widgets ensemble.
Tous ceux qui se trouvent dans la même boîte ne peuvent être
préalablement écartés, validés, ou changer de revêtement. À la
place, le résultat est mis en cache dans un vertex buffer.
Dès
qu'un des widgets de la boîte est invalidé, les données mises en
cache sont supprimées et le widget peut à nouveau être mis à jour
et changer de revêtement. Si le coût de rafraîchissement du cache
est élevé à l'échelle d'une seconde, il est bien plus avantageux
sur le long terme.
Les
boîtes d'invalidation ont été essentielles pour rendre l'IU de
VALORANT plus performante, surtout au moment de préparer le
lancement.
Mais elles ne sont pas non plus sans désavantage :
- Les développeurs doivent déterminer quels widgets ont intérêt à être groupés ensemble dans une même boîte. Ce n'est pas une bonne solution pour les widgets qui doivent être souvent mis à jour.
- Placer un widget dans une boîte d'invalidation doit être fait à la main par un développeur. Ce n'est donc pas faisable pour tous les widgets du jeu, un choix doit être fait pour sélectionner ceux qui en bénéficieront le plus.
Vous
pourrez en apprendre plus sur les boîtes d'invalidation dans la
documentation
(en anglais)
d'Epic.
Nous
voilà enfin assez informés pour aborder le sujet de la Global
Invalidation !
Bienvenue dans la Global Invalidation
Après
avoir lu tout ça vous êtes peut-être en train de vous demander :
« Pourquoi ne pas tout simplement mettre tous les éléments
d'IU dans une grande boîte d'invalidation ? » Eh bien,
figurez-vous que c'est exactement ce que fait la Global Invalidation
(en gros).
La
Global Invalidation tend à améliorer significativement les
performances de l'IU dans tout le jeu tout en réduisant la quantité
de travail manuel demandée aux développeurs pour placer les widgets
dans chaque boîte d'invalidation.
C'est le meilleur des deux mondes.
Malheureusement,
dans la version UE4.25 d'Unreal Engine (celle qu'utilise VALORANT),
la Global Invalidation n'est pas disponible pour tous les types de
widget. Les versions les plus récentes ont fait des progrès, mais
VALORANT ne peut pas en capitaliser immédiatement. De plus, nous
avions du mal à évaluer à
quel point
la Global Invalidation rendrait VALORANT plus rapide.
C'est
là qu'on a commencé à travailler.
POURQUOI AVONS-NOUS DÉCIDÉ D'Y INVESTIR LES EFFORTS
Fin
juillet 2021, l'équipe a décidé de tester la Global
Invalidation pendant une session interne. Nous avons apporté
quelques modifications mineures afin de régler les bugs qui auraient
empêché le test d'être valide. Mais nous savions que cela
n'empêcherait pas de nouveaux bugs d'apparaître pendant le test...
et il y en a eu un paquet.
À
la fin du test, nous en avions mis en évidence au moins 20, qui
n'étaient que les plus évidents. D'autres bugs plus subtils et plus
insidieux se tapissaient sûrement dans l'ombre, sans compter les cas
marginaux qui n'avaient pas été testés.
Mais
alors... la Global Invalidation a-t-elle prouvé qu'elle pouvait
booster les performances ? Oh oui.
D'après
les données générées par ce premier test seulement, nous avons
constaté une accélération du traitement de l'IU d'environ 35%.
(Remarque : l'IU n'est responsable que d'une partie du coût par
image.)
Mais
il restait beaucoup de questions sans réponse :
- Combien de temps prendra la résolution de tous les bugs ?
- Quelles équipes doivent être mobilisées sur ce sujet ?
- Cela doit-il passer en priorité devant d'autres améliorations prévues ? Pour être capables de sortir régulièrement du nouveau contenu, nous préparons souvent nos calendriers en avance et les tâches imprévues (même quand elles font autant envie que celle-ci) sont difficiles à intégrer à l'emploi du temps.
- La correction des bugs ne risquait-elle pas de neutraliser les gains de performance ? Cela demandait une modification importante du code, et chaque changement comportait le risque d'augmenter le coût de l'IU.
- Quand devions-nous travailler sur ce sujet ? Sachant que la Global Invalidation était déjà en train d'être développée pour les versions futures d'Unreal Engine, nous devions caler notre calendrier pour intégrer les apports d'Epic.
Au
final, nous avons jugé que nous y mettre valait le coup pour deux
raisons.
Les intégrations Unreal Engine et la question du calendrier
Si
les versions 4.26 et 4.27 d'Unreal Engine avaient bien progressé
sur la Global Invalidation, VALORANT, lui, travaille avec un
calendrier décalé. Nous évitons de nous servir du fer de lance des
innovations pour avoir le temps de voir venir les risques et garantir
la stabilité pour nos joueurs.
Puisque
nous prévoyions de rester sur Unreal Engine 4.25 pendant de
nombreux mois encore, nous n'aurions pas pu vous faire profiter des
nouvelles performances avant au moins un an. Et ça ne nous faisait
pas plaisir.
Pour
en savoir plus sur la façon dont nous abordons les dernières
versions d'Unreal Engine, lisez ce
fil Twitter (en anglais)
du directeur technique de VALORANT, Marcus Reid.
Gains de performance connus
La
Global Invalidation représentait quelque chose de réellement unique
en terme d'amélioration des performances : une
valeur mesurable.
Nous avons évalué les gains potentiels pendant nos tests internes
et la façon d'y arriver était (assez) claire.
Améliorer
la performance est difficile. On ne gagne guère plus que des
millimètres et c'est en faisant mieux petit à petit que la
performance globale s'améliore sur la durée. Il est très rare
qu'une seule modification promette des gains à deux chiffres.
L'optimisation était trop juteuse pour s'en passer.
Même
en tenant compte des gains moindres à cause de la résolution des
bugs, la Global Invalidation était l'occasion en or de vous offrir
une bien meilleure performance en peu de temps.
COMMENT AVONS-NOUS ACCOMPLI CE TRAVAIL ?
Si
nous avons mené nos premières expériences avec la Global
Invalidation fin juillet 2021, nos vrais efforts pour la
stabiliser n'ont pas commencé avant fin septembre 2021.
Intégration sélective des mises à jour d'Epic
Intégrer
les versions d'Unreal Engine 4.26 et 4.27 dans leur intégralité
était hors de question, mais puisqu'Epic avait commencé à
travailler activement sur la Global Invalidation, nous avons décidé
de nous plonger dans les milliers de notes de mise à jour pour
sélectionner celles qui seraient susceptibles de nous rapprocher de
notre objectif : une Global Invalidation fonctionnelle à 100%.
N'intégrer
qu'une partie des nouveautés n'était pas une tâche facile. Il
était important de modifier le moins de fonctionnalités centrales
possible pour maintenir la stabilité du moteur tout en profitant de
celles qui nous intéressaient chez Epic. Tout ce travail a été
effectué dans un environnement indépendant de VALORANT pour ne pas
gêner les autres développeurs.
Quand
le travail d'intégration sélective de quelques nouveautés d'Epic
dans notre moteur a été fait, nous avons encore passé plusieurs
semaines à résoudre le plus de bugs possible pendant que nous nous
préparions à introduire la Global Invalidation dans l'environnement
principal de VALORANT. En chemin, nous
avons mis en place un interrupteur d'urgence pour activer ou
désactiver la fonctionnalité (au
cas où une catastrophe arriverait).
Une
fois la plupart des bugs résolus et grâce à beaucoup de nouveautés
des versions 4.26 et 4.27 d'Epic, nous avons fusionné notre
environnement isolé avec l'environnement principal.
Trouver des points communs entre les bugs
Bien
que les bugs liés à la Global Invalidation se soient manifestés de
beaucoup de façons différentes, la racine du problème remontait
souvent à une seule cause. C'est cette cause qui valait la peine
d'être résolue, car elle pouvait éliminer plusieurs problèmes
d'un coup. Une fois, par exemple, il nous a suffi d'une modification
pour résoudre plus de 10 bugs éparpillés dans le jeu.
L'analyse méticuleuse de la racine des problèmes nous a permis de
mettre en place des solutions durables qui ont amélioré la
fiabilité et la stabilité de la Global Invalidation.
Identifier les bugs, les résoudre, lancer le test, et recommencer
Les
semaines et mois suivants ont consisté à suivre un cycle qui
commençait par activer la Global Invalidation avant un test, puis
identifier une série de bugs, désactiver la Global Invalidation à
la fin du test et résoudre ces bugs.
À
chaque nouvelle itération du cycle, nous trouvions de moins en moins
de bugs. Nous avons continué ainsi jusqu'à ce que le flot constant
de bugs se soit réduit à un petit filet, puis se tarisse
complètement.
Arrivés
fin novembre 2021, les problèmes les plus graves avaient été
résolus et la Global Invalidation était généralement stable.
Bugs notables
- Astra faisait planter le jeu : à un moment, choisir Astra menait systématiquement à un plantage au moment de lancer la partie. Ce bug a été réparé en intégrant une mise à jour d'Epic.
- Plantage de l'héritage multiple : l'héritage multiple avec C++ est un sujet sensible. Sans nous aventurer trop loin dans la jungle, disons que la destruction dans une classe en particulier ne se lançait pas dans le bon ordre et provoquait un crash. Il nous a suffit d'échanger 2 lignes de codes dans l'héritage pour résoudre le problème. Si vous souhaitez de plus longues explications sur l'héritage multiple, consultez cette page (en anglais).
- Son interminable du chat : quand vous étiez dans les menus, le son de la barre du chat quand on passe la souris dessus ne s'arrêtait plus. Et pour bien énerver tout le monde, le bug déclenchait le son plusieurs fois par seconde. Pour le résoudre, il nous a fallu comprendre à quelle fréquence les widgets traitaient les événements liés à la souris plusieurs fois par seconde.
Tester différentes méthodes
Une
des caractéristiques de la Global Invalidation qui nous a rendus
particulièrement prudents (et donc très méticuleux dans nos tests)
est qu'elle a
des effets sur tous les aspects du jeu.
Absolument tous.
Votre
liste d'amis ? Absolument. Le bouton pour entrer dans la file
d'attente ? Concerné aussi. Le menu des paramètres ?
Ouaip. Le pourcentage de vos tirs dans la tête ? Vous allez
rire...
L'idée
à retenir est qu'il y a des éléments d'IU dans tout le jeu et
qu'ils donnent aux joueurs des informations cruciales. Casser
ne serait-ce qu'un seul de ces éléments d'IU était inconcevable.
Pour
éviter cela, notre département QA a créé un programme de test qui
comportait plusieurs stratégies afin d'avoir pleine confiance en le
fonctionnement de la Global Invalidation.
Le test en tranches verticales
Une
« tranche verticale » dans VALORANT représente un chemin
emprunté le plus souvent par les joueurs, que ce soit pour lancer le
client, entrer dans la file d'attente d'une partie, jouer cette
partie jusqu'au bout ou interagir dans l'écran de fin. En se
concentrant sur ces éléments clés du jeu, la QA pouvait tester
rapidement les aspects les plus utiles du jeu et identifier les
problèmes très tôt.
Essais destructifs
Les
essais destructifs commencent là où s'arrêtent les tests en
tranches verticales. Ils servent à identifier les problèmes moins
standards, souvent en modifiant des facteurs extérieurs (comme la
vitesse du réseau, les images par seconde, en faisant Alt+Tab,
etc.). Armée d'un ensemble d'outils internes, la QA a passé
plusieurs semaines sur les essais destructifs.
Test des cas marginaux
Beaucoup
d'aspects de VALORANT ne servent que pour un pourcentage infime des
joueurs. D'autres encore ne sont faits que pour servir une seule fois
(comme le contrat du nouveau joueur). Mais ce n'est pas parce que ces
voies du jeu sont moins empruntées qu'elles sont moins importantes.
Identifier et tester tous nos cas marginaux nous a aidés à dénicher
des bugs cachés.
Tests dans le PBE (Public Beta Environment)
Le
test sur PBE était une étape importante de la Global Invalidation.
- C'était la première occasion de faire tester la fonctionnalité par le public. Elle allait pouvoir être testée « en conditions réelles ».
- Le PBE inclut une grande variété de configurations matérielles. Nous avons fait attention à ce qu'il couvre des appareils d'entrée de gamme aux plus hauts de gamme, car tester auprès d'un si large éventail de machines nous permet d'avoir une confiance solide dans les performances de la Global Invalidation chez tous nos joueurs.
Suite
au test dans le PBE effectué pendant le week-end du 22 au
23 janvier 2022, nous étions convaincus que la Global
Invalidation ne mettrait pas en péril l'intégrité du jeu et que
les gains de performances correspondaient aux résultats attendus.
Sortir la Global Invalidation
Après
avoir livré la Global Invalidation avec le patch 4.03, nous
avons surveillé de près les bugs signalés par les joueurs. Nous
avons aussi gardé un œil attentif sur les données de performances
pour nous assurer que les résultats réels étaient à la hauteur
des estimations. Au final, la Global Invalidation a été un grand
succès auprès des joueurs, et nous espérons que le meilleur taux
d'IPS vous fait plaisir.
Maintenant
que la Global Invalidation est dans la cour des grands, notre équipe
en charge de la performance retourne travailler sur toujours plus
d'optimisations. D'ici la prochaine percée, on vous souhaite de
belles éliminations !