Amélioration technique : Global Invalidation dans VALORANT

Lisez comment notre équipe technique a fait décoller les performances du client 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.

image.png

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

image.png

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 :

Screenshot_2022-03-07_at_16-30-57_PRF_-_Global_Invalidation_article.png


Quand on assemble le tout, le bloc de construction dédié au compteur de munitions ressemble à ça :

image.png

(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.

image.png


À 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 !