- Exemple de refonte complète d’une UI pour appareils de salon sur une base Rust et WebAssembly
- Une architecture conçue pour offrir hautes performances et faible latence d’entrée même sur des appareils aux niveaux de performances très variés
- Abandon de l’approche basée sur React au profit d’un SDK UI dédié en Rust développé en interne, avec un fort gain de productivité
- Gestion de la complexité du code et des performances grâce à une architecture Entity-Component-System (ECS)
- Analyse franche des avantages, inconvénients et problèmes liés à l’utilisation de WebAssembly et Rust
Pourquoi reconstruire l’UI de Prime Video avec Rust et WebAssembly
- Amazon devait relever le défi de faire fonctionner la même application Prime Video sur divers appareils de salon (consoles, décodeurs, clés de streaming, TV, etc.)
- Pour offrir une expérience utilisateur cohérente sur des appareils aux performances très différentes, un moteur d’UI très performant était indispensable
- L’architecture existante reposait sur une stack technologique mixte combinant React (TypeScript), JavaScript, C++, WebAssembly et Rust
- En raison de la lenteur d’exécution de JavaScript et des difficultés de mise à jour, la décision a été prise de migrer entièrement vers Rust
- WebAssembly facilite la mise à jour de l’application, et Rust est particulièrement adapté à l’optimisation des performances
Principaux défis de développement pour les appareils de salon
- Il fallait prendre en charge une large gamme de niveaux de performances, depuis des appareils puissants comme la PS5 jusqu’à des clés USB basse consommation
- Le développement devait se faire sur une base de code unique, sans équipe distincte pour chaque appareil
- Sur la plupart des appareils, seules les mises à jour de firmware sont possibles, sans app store, ce qui complique les mises à jour de code natif
- Pour mettre à jour fréquemment l’UI, l’utilisation de code JavaScript et WebAssembly est plus avantageuse
- Le choix de la combinaison Rust + WebAssembly représente un compromis entre fortes exigences de performance et cycle de mise à jour rapide
Comparaison entre l’ancienne architecture et la nouvelle architecture UI basée sur Rust
- L’ancienne architecture était structurée comme suit :
- logique UI écrite en React, moteur d’UI bas niveau géré en Rust (WebAssembly)
- React → bus de messages → moteur d’UI WebAssembly → backend de rendu C++
- Pour résoudre les problèmes de latence d’entrée, toute la logique métier a été migrée vers le SDK UI Rust
- Nouvelle architecture :
- de l’UI SDK jusqu’au rendu, tout est construit en Rust
- suppression du bus de messages, tout le traitement s’exécute à l’intérieur de WebAssembly
- le code est compilé en WebAssembly puis envoyé vers la TV, avec une meilleure vitesse de mise à jour et une meilleure réactivité qu’auparavant
Principaux composants du nouveau SDK UI Rust
- Adoption d’un concept de Composable similaire à React → des unités de composition UI réutilisables
- Système d’UI réactif basé sur Signal et Effect
- Signal : lorsqu’une valeur change, il déclenche les Effect associés
- Memo : ne réagit que si la valeur diffère de la précédente
- La hiérarchie UI est définie via la macro
compose!
- Les éléments d’UI se composent de Widget (composants fournis) et de Composables (structures définies par l’utilisateur)
- Utilisation d’une architecture Entity-Component-System (ECS) :
- Entity : ID
- Component : données d’attributs (ex. Layout, RenderInfo, Text)
- System : fonction exécutant une logique sur une combinaison donnée de Component
Structure et fonctionnement du système ECS
- Chaque système requiert une combinaison spécifique de composants et s’appuie dessus pour traiter les mises à jour de l’UI
- Exemples :
- Resource Management System : composant image → upload GPU → mise à jour de RenderInfo
- Layout System : calcul de divers composants liés au layout
- Rendering System : affichage réel à l’écran à partir de RenderInfo
- Cette structure permet une migration progressive de différentes pages de React vers Rust
- La coexistence et la transition entre pages basées sur JavaScript et pages basées sur Rust se font sans difficulté
Résultats positifs et avantages
- Même les développeurs JavaScript/React ont pu passer au SDK UI Rust sans perte de productivité
- Grâce à la structure familière du SDK UI, même les débutants en Rust peuvent s’adapter rapidement
- Il est désormais possible d’implémenter des fonctionnalités auparavant impossibles, comme les animations de layout et les transitions d’écran rapides
- Les outils internes de développement (resource manager, layout inspector, etc.) peuvent eux aussi être créés rapidement sur une base Rust
- La latence d’entrée est passée de 250 ms à 33 ms (sur les appareils les moins puissants)
Difficultés rencontrées et limites techniques
- WebAssembly System Interface (WASI) reste un écosystème en évolution, avec un risque que des mises à jour de Rust cassent du code existant
- Dans WebAssembly, un panic met fin à toute l’application, ce qui complique la garantie de stabilité
- contrairement à JavaScript, la gestion des exceptions est insuffisante → nécessité d’utiliser activement le type
Result
- en cas de dépendance à des bibliothèques externes, il faut encourager des implémentations sans panic
- Dans l’environnement navigateur, WebAssembly et certaines API de rendu ne sont pas prises en charge, donc cette approche n’est pas appliquée au client web
Bytecode Alliance et contribution à l’écosystème
- Amazon participe activement, en tant que membre de la Bytecode Alliance, à la standardisation de WASI et à l’amélioration des fonctionnalités associées
- Le WebAssembly Micro Runtime utilisé est basé sur C, et Wasmtime, basé sur Rust, est également étudié en parallèle
- Amazon contribue directement à l’évolution de l’écosystème WebAssembly via des retours techniques et une participation au développement
Autres Q&R
- Est-ce possible aussi dans un navigateur web ? → certains navigateurs WebKit ne prennent pas en charge WASM, les performances baissent, et la complexité d’implémentation reste élevée, donc cela est encore à l’étude
- Une implémentation avec WebGL est possible, mais le rapport coût/bénéfice est jugé insuffisant pour le moment, donc l’idée est mise en attente
Résumé
- L’UI de Prime Video basée sur Rust + WebAssembly réunit hautes performances, faible latence d’entrée et mises à jour rapides
- Le SDK UI maison et l’architecture ECS permettent de gérer efficacement des comportements UI complexes
- Même si l’adoption de Rust n’est pas simple, une conception méthodique et une culture de développement adaptée permettent de concilier productivité et stabilité
- L’écosystème WebAssembly est encore en développement, mais il est déjà suffisamment viable pour un service réel
- Cette adoption réussie repose sur un prototypage rigoureux et une stratégie de migration progressive
2 commentaires
Par rapport au front-end qui embarque par défaut une bibliothèque de gestion d’état, j’ai tendance à penser que le jeu, justement, fonctionne un peu en mode « à la dure ? », puisque tous les états interagissent avec tous les autres. À l’inverse, utiliser l’ECS dans une application revient sans doute à adopter une gestion d’état modélisée, un peu comme quand chaque développeur utilise ses propres patterns ou une bibliothèque interne ; je serais curieux de savoir comment cet aspect a été traité.
Appliquer à l’UI un ECS qu’on voyait plutôt dans les moteurs de jeu, voilà une idée assez originale. Aujourd’hui encore, j’aurai appris quelque chose.