WebXR/WebGLアプリをどのようにプロファイリングするか
この記事では、WebXRおよびWebGLアプリ(基礎フレームワークに依存しない)のプロファイリング方法を説明します。プロファイリングはアプリケーションを最適化するための第一歩です。プロファイリングをせずに最適化するのは無駄な努力になります。ボトルネックに記載されているように。
この記事では、見つかったパフォーマンス問題を修正する方法には触れていません。特定のボトルネックを対象とした多くのブログ投稿や講演がありますが、問題がわかれば、それに対するリソースを見つけることができます。
私たちがプロファイル化する指標には、CPU時間、GPU時間、JavaScriptヒープメモリ、アプリケーションのロード時間があります。目標は、プロファイリングに関する最も包括的な情報を無料で提供することです。また、「ポリカウント」は_重要な指標ではない_ということもあります!
コンテンツ
ボトルネック
アプリのボトルネックを見つけなければ、最適化の努力は効果的ではありません。
例
多くのオブジェクトを含む大きなシーンをレンダリングしていると想像してください。アプリはかろうじて60 fpsに達しています。オブジェクトの頂点数をすべて最適化しても、60 fpsにとどまる可能性があります。
CPUは、GPUに各オブジェクトをレンダリングするコマンド(「Draw Call」)を送信する役割を果たします。この場合、CPUがドローコールを送信する作業に追われている可能性が高く、GPUはCPUがより多くの作業を送信するのを待って指を動かしています。
しかし、測定しない限りわかりません。それがプロファイリングの目的です。
ボトルネックの種類
アプリケーションのどの部分でもボトルネックを引き起こす可能性があります。よくあるボトルネックの例を頻度の順に示します:
CPU ドローコール: ドライバへの呼び出しが多すぎてオーバーヘッドが大きすぎる。
ガベージ: アプリケーションは正常に動作しているが、定期的または不定期にヒッチを引き起こすガベージを生成している。
CPU ロジック: アプリケーションがGPUに対する作業を十分に迅速に生成できない。重い物理シミュレーションやレイキャストによって引き起こされることが多い。
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にはメモリ管理が組み込まれており、割り当てを気にすることなく扱うことができます。そのため、多くの場合、そのように行います。
メモリを管理する必要がないと、メモリを管理したいと指定する制御を失います。このプロセスは"Garbage Collection"と呼ばれ、アプリケーションライフサイクルのランダムな瞬間に発生します。例:V-Syncを守り、フレームを提出しようとしたとき。
ガベージコレクションには0.1〜10 msかかる場合があります。通常のVRフレームバジェットが11msであると考えると、これは絶対にランダムな瞬間に発生してほしくないものです。
では、どうすればこれを避けられるでしょうか?唯一の方法は、クリーンアップが必要なガベージを全く生成しないことです。生成するものが少なければ、ガベージコレクションのヒッチが小さくなり、あまり頻繁には発生しません。
アプリケーションのロード時間
ウェブサイトのロードと同様に、ユーザーのアプリの使用へのコミットメントは、待っている時間が長くなるにつれて減少します。WebXRアプリケーションはかなり大きいため、ウェブサイトに比べて少し良い意志があるかもしれませんが、必要はありません。
アプリケーションを早く開始し、すぐには必要でないリソースを後からロードすることで、体感ロード時間を短縮できます。
資産を最適化し、サーバー設定が最適であることを確認することで、ロード時間全般を短縮できます。
また、処理が少ない形式を使用することで、リソースのダウンロード後にCPUが行う必要のある作業を減らすことができます。
ツール
この記事では、次のツールについて説明します:
- Chrome Profiler, [CPU, GPU, Garbage]
- Chrome Networkingタブ, [Loading Time]
- OVR Profiler Tool, [CPU, GPU]
- Spector.jsブラウザ拡張機能, [CPU, GPU]
- WebGL Disjoint Timer Query, [GPU]
- Wonderland Editor Profiler (Wonderland Engineのみ). [CPU, GPU]
Chrome Profiler
Chromeの組み込みプロファイラーを使用すると、JavaScriptのCPU時間、ガベージコレクション、および非常におおまかなGPUフレーム時間をプロファイル化できます。
Ctrl + Shift + C
(MacOSではCommand + Shift + C
)を押すと、任意のウェブサイトのパフォーマンスタブが見つかります。「Performance」を見つけてください。
プロファイルセッションを記録するには、左上のレコードボタンを押します(Ctrl/Command + E
)。
通常、3〜5秒で十分です。私たちは通常、単一のフレームに関心があります。
これは、Androidデバイスでのリモートデバッグの際にも機能します。 Meta Questやスマートフォンのように。
Safariも、Mac、iOSデバイス、およびApple Vision PRO(例えば、Apple Vision PROシミュレーターでSafariを実行している場合)向けに同様のプロファイラーを提供しています。
Chrome Memory Profiler
ガベージコレクションでは、通常はスムーズに動作しているアプリケーションにおけるスタッターを説明します。
そのボトルネックを見つけるために、ブラウザはJavaScriptヒープをサンプリングする方法を提供しています。これが、前述のChrome Profilerでの有効化方法です:

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

結果として、ドロップされたフレームが前のフレームから再投影されることを期待する必要があります。そうしないと、重大なスタッターが発生します。 アニメーションが再投影されることはないので、どちらにしてもスタッターが発生します。
Networkingタブ
ブラウザのネットワーキングタブは、アプリケーションのロード時間をプロファイル化するための優れたツールです。
Ctrl + Shift + C
(MacOSではCommand + Shift + C
)を押して任意のウェブサイトをナビゲートすると、ネットワーキングタブが見つかります。「Networking」を見つけてください。
ネットワーキングアクティビティを記録するには、タブを開いた状態でページを再読み込みします。
ダウンロードのブロック
リソースがアプリケーション開始時にすぐには必要でない場合は、後でロードされるべきです。ブラウザは同時に実行される並列リクエストが制限されているためです(例:Chromeの場合は6)。
Meta Quest Performance HUD
Meta QuestでVRレンダリングをプロファイル化する場合は、Performance HUDを使用できます。
これはMeta Quest Developer Hubから最も簡単にインストールできます。
これは、ヘッドセットを使用しながらパフォーマンスに関する継続的なフィードバックを得るために、ビューに依存するパフォーマンスの問題をデバッグするのに特に役立ちます。
例
以下の例では、Ayushman JohriがWonderland Engineベースの「Vintage Study Room」の優れたパフォーマンスをMeta Quest Performance HUDを使用して示します:
こちらは @MetaQuestVR のFPSグラフ付きのパフォーマンステストです!
— Ayushman Johri ✨ (@AyushmanJohri) August 19, 2023
システム録画がなければ、より多くの静止フレームが観察されることを言及します~
また、アーティスティックな選択として、いくつかの詳細なアセットのために非圧縮テクスチャを使用することに決めました ✨
完璧ではないが、非常に近いです! pic.twitter.com/MLzWVLHItn
OVR GPU Profiler
頂点またはフラグメントシェーディングのボトルネックに直面した場合、シェーダー内で最も時間がかかる部分をより明確に理解することが役立ちます。
ovrgpuprofilerは非常に鋭いツールです。GPUメモリとアーキテクチャの理解がある場合、それは多くの洞察を提供します。
それはadb
を介してMeta Questヘッドセットにインストールし、adb shell
を介して実行されます:
1 47 metrics supported:
2 1 Clocks / Second
3 2 GPU % Bus Busy
4 3 % Vertex Fetch Stall
5 4 % Texture Fetch Stall
6 5 L1 Texture Cache Miss Per Pixel
7 6 % Texture L1 Miss
8 7 % Texture L2 Miss
9 8 % Stalled on System Memory
10 9 Pre-clipped Polygons/Second
11 10 % Prims Trivially Rejected
12 11 % Prims Clipped
(ソース:developer.oculus.com)
出力例は次のようになります:
1$ adb shell ovrgpuprofiler -r"4,5,6"
2
3% Texture Fetch Stall : 2.449
4L1 Texture Cache Miss Per Pixel : 0.124
5% Texture L1 Miss : 20.338
6
7% Texture Fetch Stall : 2.369
8L1 Texture Cache Miss Per Pixel : 0.122
9% Texture L1 Miss : 20.130
10
11% Texture Fetch Stall : 2.580
12L1 Texture Cache Miss Per Pixel : 0.127
13% Texture L1 Miss
14
15...
(修正されました:developer.oculus.com)
シェーダープログラムで最も高価な操作がメモリの読み書きであると言われることがよくあります。これが真実かどうかは、メモリ操作がどれほどキャッシュフレンドリーであるかによります。
L1(「レベル1」)キャッシュメモリは非常に高速です。それを確実に使用できるようにするためには、参照の局所性の原則を守り、以前にアクセスしたメモリに近いメモリにアクセスすることを確認してください。
Spector.js
Spector.js はWebGLフレームトレースをキャプチャするためのChromeおよびFirefox向けのブラウザ拡張機能です。このツールはコマンドの完全な一覧を表示し、頂点数や描画コールなどの統計を要約します。
Chrome拡張機能ストア またはFirefoxアドオンライブラリからインストールできます。 プラグインサポートがないブラウザではHTMLタグを介してツールを埋め込むこともできます。

まず、上部のアドオンボタンでツールを有効にし、その後、赤い記録ボタンをクリックしてフレームを記録します。

ここでは、Wonderland Engineが多くのオブジェクトを合計11の描画コールで描画していることがわかります。 他のフレームワークの場合、この数は10〜100倍高くなるでしょう。
Disjoint Timer Query
EXT_disjoint_timer_query extension は、WebGLコマンドのセットのGPU時間を測定するWebGL拡張機能です。これはChromeでのみ(2023年8月時点)サポートされ、“WebGL Debug Extensions” Chromeフラグを有効にする必要があります。
すべてのコマンドはGPU上で非同期に実行されるため、時間測定も非同期にスケジュールする必要があります。
Wonderland Editor Profiler
Wonderland Editorには、WebXRアプリのパフォーマンスがどのように苦しんでいる可能性があるかを理解するのに役立つ組み込みプロファイリングツールが付属しています。
終わりの言葉
すべてのフレームワークには特有のパフォーマンス特性があります。ほとんどは、他の何よりもまず描画コールに制約され、その次にフラグメントに制約されます。90 fpsでない場合は低ポリにする必要はありません!
Wonderland Engineは、上記のボトルネックのほとんどを回避するようにゼロから設計されており、年間120,000米ドルの収益まで無料です。 エンタープライズライセンスとサポートについてはこちらにご連絡ください。
Wonderland Engineを試してみてくださいそして、最適化に費やす時間を節約しましょう。
