- リビング向けデバイス用UIをRustとWebAssemblyベースで全面再構成した事例
- さまざまな性能レベルのデバイスでも高性能と低い入力遅延を実現するための構造を設計
- Reactベースから脱却し、Rust専用UI SDKを独自開発して高い生産性を確保
- Entity-Component-System (ECS) ベースのアーキテクチャにより、コードの複雑さと性能を管理
- WebAssemblyとRustの採用による長所・短所および課題を率直に分析
Prime Video UIをRustとWebAssemblyで再構成した理由
- Amazonは、さまざまな**リビング向けデバイス(コンソール、セットトップボックス、ストリーミングスティック、TVなど)**で同一のPrime Videoアプリを動作させる必要があった
- 性能が異なるデバイス群に対して一貫したユーザー体験を提供するには、高性能なUIエンジンが不可欠だった
- 従来は、React(TypeScript)、JavaScript、C++、WebAssembly、Rustの混在した技術スタックを使用していた
- JavaScriptの実行速度の遅さと更新の難しさにより、全面的にRustへ移行することを決定
- WebAssemblyを活用するとアプリの更新が容易になり、Rustは性能最適化に有利だった
リビング向けデバイス開発における主な課題
- PS5のような高性能機から低電力のUSBスティックまで、多様な性能スペックへの対応が必要
- 各デバイスごとに別チームを置かず、単一のコードベースで開発しなければならない
- ほとんどのデバイスではアプリストアがなく、ファームウェア更新のみ可能なため、ネイティブコードの更新が難しい
- UIを頻繁に更新するには、JavaScriptおよびWebAssemblyベースのコード利用が有利
- 高性能要求と素早い更新サイクルのバランス点として、Rust + WebAssemblyの組み合わせを選択
既存アーキテクチャと新しいRustベースUIアーキテクチャの比較
- 既存アーキテクチャは次のような構成だった:
- ReactでUIロジックを記述し、Rust(WebAssembly)は低レベルのUIエンジンを処理
- React → メッセージバス → WebAssembly UIエンジン → C++レンダリングバックエンド
- 入力遅延の問題を解決するため、すべてのビジネスロジックをRust UI SDKへ移行
- 新しいアーキテクチャ:
- UI SDKからレンダリングまですべてRustで構成
- メッセージバスを廃止し、すべての処理をWebAssembly内部で実行
- コードはWebAssemblyにコンパイルされてTVへ配信され、従来より更新速度と応答性が向上
新しいRust UI SDKの主要構成要素
- Reactに似たComposableの概念を導入 → 再利用可能なUI構成単位
- SignalとEffectベースのリアクティブUIシステム
- Signal: 値が変更されると関連するEffectをトリガーする
- Memo: 以前の値と異なる場合にのみ反応
- UI階層構造は
compose!マクロで定義
- UI要素はWidget(組み込みコンポーネント)とComposables(ユーザー定義構造)で構成
- Entity-Component-System(ECS) アーキテクチャを採用:
- Entity: ID
- Component: 属性データ (ex. Layout, RenderInfo, Text)
- System: 特定のComponentの組み合わせに対してロジックを実行する関数
ECSシステム構造と動作方式
- 各システムは特定のコンポーネントの組み合わせを必要とし、それに基づいてUI更新を処理
- 例:
- Resource Management System: 画像コンポーネント → GPUアップロード → RenderInfo更新
- Layout System: さまざまなレイアウト関連コンポーネントを計算
- Rendering System: RenderInfoに基づいて実際の画面出力を実行
- この構造により、さまざまなページをReactからRustへ段階的にマイグレーション可能
- JavaScriptベースのページとRustベースのページの共存と切り替えが円滑
良かった結果と利点
- JavaScript/React開発者もRust UI SDKへ生産性を落とさず移行に成功
- UI SDKの親しみやすい構造のおかげで、Rust初心者も素早く適応可能
- レイアウトアニメーション、高速な画面遷移など、以前は不可能だった機能を実装可能
- 内部開発ツール(リソースマネージャー、レイアウトインスペクターなど)もRustベースで迅速に開発可能
- 250msだった入力遅延を33msまで大幅削減(低スペック機基準)
難しかった点と技術的限界
- WebAssembly System Interface(WASI) はまだ発展途上のエコシステムであり、Rust更新時に既存コードが壊れる可能性がある
- WebAssemblyではpanic発生時にアプリ全体が終了するため、安定性の確保が難しい
- JavaScriptと異なり例外処理が不十分なため、
Result型の積極的な活用が必要
- 外部ライブラリに依存する場合はpanic-freeな実装を促す必要がある
- ブラウザ環境ではWebAssemblyおよび特定のレンダリングAPIが未対応のため、Webクライアントには未適用
Bytecode Allianceとエコシステムへの貢献
- AmazonはBytecode Allianceの一員として、WASI標準化と関連機能改善に積極的に参加
- 使用中のWebAssembly Micro RuntimeはCベースであり、RustベースのWasmtimeも並行して検討
- WebAssemblyエコシステムの発展に向けて、直接技術フィードバックと開発参加を継続中
その他のQ&A
- Webブラウザでも可能か? → 一部のWebKitブラウザはWASM未対応で、性能低下や実装の複雑さもあり、現時点では検討段階
- WebGLでの実装は可能だが、現時点では投資対効果が低いと判断して保留
要約
- Prime VideoのRust+WebAssemblyベースUIは、高性能、低入力遅延、高速な更新という3つの要件を満たす
- 独自UI SDKとECSアーキテクチャは、複雑なUI動作を効率的に管理する
- Rust導入は容易ではないが、体系的な設計と開発文化により生産性と安定性を両立
- WebAssemblyエコシステムはまだ発展途上だが、実サービスでも十分に実現可能
- 成功した導入は、徹底したプロトタイピングと段階的な移行戦略に基づいている
2件のコメント
状態管理ライブラリを前提にしたフロントエンドと比べると、ゲームこそあらゆる状態があらゆる状態に干渉するので、わりと力業でやっているものだと思っていたのですが、逆にアプリケーションでECSを使うというのは、パターン化された状態管理を各開発者あるいは独自ライブラリで使うのに近いはずで、そのあたりをどうしていたのか気になりますね。
ゲームエンジンで見かけるようなECSをUIに適用するとは、これはちょっと斬新な発想ですね。今日もまた一つ勉強になりました。