WebXR/WebGLアプリのプロファイリング方法
この記事では、WebXRとWebGLアプリのプロファイリング方法について説明します(基盤のフレームワークに依存しません)。プロファイリングは、アプリケーションを最適化するための最初のステップです。プロファイリングを行わずに最適化することは、ボトルネックに記載されているように、無駄な努力です。
この投稿では、発見されたパフォーマンスの問題を解決する方法については触れていません。特定のボトルネックに焦点を当てたブログ投稿や講演は多くありますが、問題がわかれば、それを解決するためのリソースを見つけることができます。
プロファイリングする指標は、CPU時間、GPU時間、JavaScriptヒープメモリ、アプリケーションのロード時間です。プロファイリングのための包括的な情報を無料で提供することを目指しています。そして、「ポリカウント」は_私たちが気にする指標ではありません_!
### 内容ボトルネック
アプリのボトルネックを見つけないと、最適化の効果は得られません。
例
多くのオブジェクトを持つ大きなシーンをレンダリングしていると想像してみてください。アプリはかろうじて60 fpsに達しています。各オブジェクトの頂点数を最適化しても、60 fpsのままかもしれません。
CPUは、各オブジェクトをレンダリングするためのコマンドをGPUに送る役割を担っています(「ドローコール」)。この場合、CPUがドローコールを大量に送信するために忙しすぎて、GPUは次に送る仕事を待っているだけかもしれません!
しかし、それは測定して初めてわかることです。これがプロファイリングの役割です。
ボトルネックの種類
アプリケーションのあらゆる部分がボトルネックを引き起こす可能性があります。以下は一般的なボトルネックの例です(発生頻度の順に並んでいます):
CPUドローコール: ドライバーへのコールが多すぎてオーバーヘッドが大きい。
ガーベージ: アプリケーションは正常に動作しているが、定期的または不規則にカクつきを引き起こすガーベージを生成している。
CPUロジック: アプリケーションは、不十分な速度でGPUに仕事を生成している。しばしば、高度な物理シミュレーションやレイキャストが原因で発生します。
GPUフラグメントシェーディング: フラグメント単位でのパフォーマンスのコストが高すぎる。
GPU頂点処理: 頂点処理の必要性が高すぎる。
GPU解決: 移動デバイスでのポストプロセッシングによって引き起こされる。
GPU頂点フェッチ: 頂点のメモリが十分に速く読み込まれない。
それぞれがサブボトルネックを持つことがあります。
指標
プロファイリングする指標は以下です:
CPU時間
XRアプリケーションの作業はCPUとGPUの間で分割されます。CPUはGPUがレンダリングするためのグラフィック作業を準備し、アプリのロジックを実行します。
すべてのJavaScriptコードは、ドローコールの準備、リソースの読み込み、物理シミュレーション、オーディオのレンダリング、シーングラフの変換の計算を行うためにCPU上で実行されます。
GPU時間
GPUはグラフィックスの重作業を担当します。CPUは「メッシュXをシェーダーYとテクスチャZとマテリアルパラメーターWで描画する」といったドローコールを送り、GPUは実際のラスター化と頂点ごとの変換を行い、画面上にピクセルを作成します。
GPUはまた、最終的な画像を画面に送信し、アプリケーションのリフレッシュレートと画面のリフレッシュレートを同期する”V-Sync”を待機します。
フレームレートとV-Sync
フレームレート(毎秒フレーム数=fps)を使用してアプリケーションをプロファイリングすることはありません。
この指標は、パフォーマンスとV-Syncの相互作用を理解するための複雑さを説明するためにのみ挙げています。判断を行うには粗すぎます。
V-Syncを_固定されたデッドライン_として考え、1秒間に60、72、90以上のデッドラインがあります(フレームレート)。期限までにフレームをレンダリングして提出することを「V-Syncを達成する」として言及します。
期限を逃すと、すべての作業が破棄され、次の期限に追いつくために努力しなければなりません。柔軟なフレームレート環境では、ドライバーがフレームレートを半分に落とす可能性があります。
アプリケーションがほんの少し遅ければ、59 fpsの代わりに例えば30 fpsが表示され、全く異なる状況になります。アプリケーションが3msのCPUタイムと3msのGPUタイムでそれなりに良好に動作している場合でも、作業が遅れて開始されるためにV-Syncを逃し、目標のフレームレートが実際には半分になってしまうことがあります。この場合も、頂点数やドローコール、または古典的な最適化はいずれも役に立たないかもしれません。
レイテンシー
入力(例:頭部の動き)と完成したフレームの間の時間であるレイテンシーは、特にVRにおいて重要です。WebXRの実装は通常これを処理し、フレーム予算を使用しない場合、レイテンシーを減らすためにフレームコールバックを少し遅らせることがあります。このブログ投稿ではWebXRからこれを管理できる範囲が限られているため、レイテンシーについては取り上げません。
ガーベージコレクション
JavaScriptは、メモリ管理を提供し、割り当てを気にせず無頓着に扱うことができます。従って、多くの場合そうしてしまうことが普通です。
メモリを管理する必要がないため、メモリが管理されるときに何をしたいかを指定する制御が失われます。このプロセスは「ガーベージコレクション」と呼ばれ、アプリケーションのライフサイクル中にランダムに発生します。例えば、V-Syncを達成してフレームを提出しようとした直前などです。
ガーベージコレクションは0.1 - 10 msかかることがあります。通常のVRフレーム予算が11 msであることを考えれば、タイミングを無視すると、このプロセスがランダムに発生するのは絶対に避けたいことです。
では、どうすればこれを避けることができるのでしょうか?唯一の方法は、クリーンアップを必要とする_どんな_ガーベージも避けることです。生成する量が少ないほど、ガーベージコレクションの発生が少なくなり、その影響は軽減されます。
アプリケーションのロード時間
ウェブサイトのロードと同様に、ユーザーがアプリを使用する意欲は待ち時間が増えるほど減少します。WebXRアプリケーションは比較的大きいため、ウェブサイトと比べてある程度の猶予があるかもしれませんが、それでも不要です。
アプリケーションを早期に開始し、すぐに必要でないリソースを後で読み込むことにより、体感的なロード時間を短縮できます。
アセットを最適化し、サーバー設定を最適化することで全体的なロード時間を短縮できます。
そして、解析に時間がかからないフォーマットを使用することで、ダウンロード後にCPUが行う作業量を減らすことができます。
ツール
この投稿では、次のツールを取り上げます:
- Chromeプロファイラー, [CPU, GPU, ガーベージ]
- Chromeネットワーキングタブ, [ロード時間]
- OVRプロファイラーツール, [CPU, GPU]
- Spector.jsブラウザ拡張機能, [CPU, GPU]
- WebGL Disjoint Timer Query, [GPU]
- ワンダーランドエディタープロファイラー (ワンダーランドエンジン専用). [CPU, GPU]
Chromeプロファイラー
Chromeの内蔵プロファイラを使用すると、JavaScriptのCPU時間、ガーベージコレクション、非常におおまかなGPUフレーム時間をプロファイルすることができます。
パフォーマンスタブを見つけるには、任意のウェブサイトにアクセスし、Ctrl + Shift + C
(MacOSではCommand + Shift + C)を押してください。「パフォーマンス」を見つけてください。
プロファイルセッションを記録するには、左上の録画ボタンを押します(Ctrl/Command + E)。
通常は3-5秒で十分です。通常私たちは単一のフレームに興味があります。
これはAndroidデバイスでのリモートデバッグの際にも動作します。たとえば、Meta Questやスマートフォンで使用できます。
Safariには、Mac、iOSデバイス、およびApple Vision PRO(たとえばApple Vision PROシミュレーターで実行中のSafari)のための類似のプロファイラーがあります。
Chromeメモリプロファイラー
ガーベージコレクションでは、順調に動作しているアプリケーションにおけるスタッターについて説明します。
そのボトルネックを見つけるために、ブラウザはJavaScriptヒープをサンプリングする方法を提供しています。これは、上で説明したChromeプロファイラーからの有効化方法です:

例
以下は、Elysian(Three.jsをベースにした)のMeta Quest 2でのプロファイルです:
“JS Heap”が14.1 MB - 23.3 MBの間で変動していることがわかります。
メモリが突然下がる場所に、ガーベージコレクションが発生しています。 この場合、GCが非常に大きく、次のフレームの開始が遅れ、フレームが落とされています:

その結果、フレームが最後のフレームから再投影されることを期待する必要があります。さもないと、深刻なスタッターが発生します。アニメーションは再投影できないため、どちらにせよスタッターが発生します。
ネットワーキングタブ
任意のブラウザのネットワーキングタブは、アプリケーションのロード時間をプロファイルするための優れたツールです。
Ctrl + Shift + C(MacOSではCommand + Shift + C)を押して任意のウェブサイトにアクセスし、「ネットワーキング」を見つけてください。
ネットワークアクティビティを記録するために、タブを開いたままページを再読み込みしてください。
ダウンロードのブロック
アプリケーション開始時にすぐに必要ない資源は、後で読み込むべきです。ブラウザは、同時に実行できる限られた並行リクエストを持っているからです(例えばChromeで6)。
Meta QuestパフォーマンスHUD
Meta QuestでVRレンダリングをプロファイルする際には、パフォーマンスHUDを利用できます。
これはMeta Quest Developer Hubを通じて最も簡単にインストールできます。
ヘッドセットを使用しながら性能に関する継続的なフィードバックを得ることができるため、視点依存の性能問題をデバッグするのに非常に便利です。
例
以下の例では、Ayushman Johriが、Meta QuestパフォーマンスHUDを使用して自身のワンダーランドエンジンベースの「ヴィンテージスタディルーム」の優れたパフォーマンスを示しています:
ここで、@MetaQuestVR FPSグラフを使用したパフォーマンステストです!
— Ayushman Johri ✨ (@AyushmanJohri) 2023年8月19日
システムの録画がなければもっと古いフレームは少なく観察されるかもしれないと私は言っておくべきです〜
また芸術的な選択として、私はいくつかの詳細なアセットのために非圧縮テクスチャを使用することを決めました ✨
完璧ではありませんが、かなり近い! pic.twitter.com/MLzWVLHItn
OVR GPUプロファイラー
頂点やフラグメントシェーディングのボトルネックに遭遇した場合、シェーダー内で最も時間がかかる部分をより明確に理解するために役立ちます。
ovrgpuprofilerは非常に鋭いツールです。GPUメモリとアーキテクチャについての知識があれば、多くの洞察を得ることができます。
これは、Meta Questのヘッドセットにadb経由でインストールし、adb shell経由で実行します:
47 metrics supported:
1 Clocks / Second
2 GPU % Bus Busy
3 % Vertex Fetch Stall
4 % Texture Fetch Stall
5 L1 Texture Cache Miss Per Pixel
6 % Texture L1 Miss
7 % Texture L2 Miss
8 % Stalled on System Memory
9 Pre-clipped Polygons/Second
10 % Prims Trivially Rejected
11 % Prims Clipped (出典:developer.oculus.com)
次のような出力が得られます:
$ adb shell ovrgpuprofiler -r"4,5,6"
% Texture Fetch Stall : 2.449
L1 Texture Cache Miss Per Pixel : 0.124
% Texture L1 Miss : 20.338
% Texture Fetch Stall : 2.369
L1 Texture Cache Miss Per Pixel : 0.122
% Texture L1 Miss : 20.130
% Texture Fetch Stall : 2.580
L1 Texture Cache Miss Per Pixel : 0.127
% Texture L1 Miss
... (変更:developer.oculus.com)
シェーダープログラムにおいて、通常、メモリの読み書きが最も高価な操作であると言われることが多いですが、これはメモリアクセスがどれだけキャッシュフレンドリーであるかに依存します。
L1(“Level 1”)キャッシュメモリは非常に高速です。それを使用するためには、参照の局所性の原則を守り、以前にアクセスしたメモリに近いメモリにアクセスすることが大切です。
Spector.js
Spector.jsは、WebGLフレームトレースをキャプチャするためのChromeとFirefox用のブラウザ拡張機能です。ツールは、コマンドの全リストを表示し、頂点カウントやドローコールのような統計情報を要約します。
Chrome拡張ストアやFirefoxアドオンライブラリからインストールできます。 プラグインサポートのないブラウザのためにHTMLタグ経由でツールを埋め込むこともできます。

まず、上のアドオンボタンからツールを有効にして、次に赤い録画ボタンをクリックしてフレームを録画します。

この場合、ワンダーランドエンジンは合計11のドローコールで多くのオブジェクトを描画しています。他のフレームワークを使用すると、この数字は10-100倍になるでしょう。
Disjoint Timer Query
EXT_disjoint_timer_queryエクステンションは、一連のWebGLコマンドのGPU時間を測定するためのWebGLエクステンションです。[2023年8月]現在ではChromeでのみ広くサポートされており、「WebGL Debug Extensions」Chromeフラグが有効になっている場合のみです。
すべてのコマンドはGPU上で非同期に実行されるため、タイム測定も非同期にスケジュールされる必要があります。
ワンダーランドエディタープロファイラー
ワンダーランドエディターには、WebXRアプリのパフォーマンスが苦しんでいる可能性のある場所を理解するのに役立つ内蔵プロファイリングツールがあります。
締めの言葉
フレームワークごとに特有の性能特性があります。多くの場合、他のものに比べてドローコールで結びつかれ、次にフラグメントで結びつかれる—90 fpsに達していない限り低ポリゴン化する必要がありません!
私たちは、上記のボトルネックをできる限り回避するためにワンダーランドエンジンをゼロから設計しました。 年間収益が120k USD未満の場合は無料です。 エンタープライズライセンスやサポートに関するお問い合わせは、こちらまでご連絡ください:[email protected]。
ワンダーランドエンジンを試すことで、最適化の時間を節約しましょう。
