Mixed Reality のパフォーマンスを理解する
この記事では、Mixed Reality アプリにおけるパフォーマンスの重要性について説明します。 アプリケーションが最適なフレーム レートで動作しないと、ユーザー エクスペリエンスは大きく損なわれる可能性があります。 ホログラムの表示が不安定になったり、環境のヘッド トラッキングが不正確になると、ユーザーのエクスペリエンスが低下します。 パフォーマンスは、Mixed Reality 開発における最後の調整作業ではなく、最も重要な機能であると考える必要があります。
各ターゲット プラットフォームにおけるパフォーマンスの目標フレーム レートの値は以下のとおりです。
プラットフォーム | 目標フレーム レート |
---|---|
HoloLens | 60 FPS |
Windows Mixed Reality Ultra PC | 90 FPS |
Windows Mixed Reality PC | 60 FPS |
以下のフレームワークは、目標フレーム レートを達成するためのベストプラクティスをまとめたものです。 Unity 環境でフレーム レートを測定および改善するためのヒントについては、「Unity のパフォーマンスに関する推奨事項」の記事を一読することをお勧めします
パフォーマンスのボトルネックを理解する
アプリのフレーム レートのパフォーマンスが低い場合、最初にとるべきステップは、アプリケーションのどこに計算負荷がかかっているかを分析して理解することです。 シーンのレンダリング作業は、CPU と GPU という 2 つの主要なプロセッサが担当しており、それぞれが Mixed Reality アプリのさまざまな側面の処理を行っています。 ボトルネックが発生する可能性のある 3 つの主要な箇所は以下の通りです。
- アプリ スレッド - CPU - 入力、アニメーション、物理演算、他のアプリ ロジックの処理など、アプリのロジックの処理を行います。
- レンダー スレッド - CPU から GPU - ドロー コールを GPU に送信する役割を担います。 このスレッドは、アプリでキューブやモデルなどのオブジェクトをレンダリングする際に、その操作を行うための要求を GPU に送信します。
- GPU - 一般的には、3D データ (モデル、テクスチャなど) をピクセルに変換するアプリケーションのグラフィック パイプラインの処理を行います。 最終的には、デバイスの画面に表示する 2D 画像を生成します。
一般的に、HoloLens アプリケーションは GPU にバインドされますが、そうでない場合もあります。 以下のツールとテクニックを使用して、ご自身のアプリのどこがボトルネックになっているのかを把握しましょう。
アプリケーションを分析する方法
Mixed Reality アプリケーションのパフォーマンス プロファイルと潜在的なボトルネックを理解するためのツールは数多くあります。
アプリケーションの詳細なプロファイル情報を収集するための一般的なツールを次に示します。
- Intel Graphics Performance Analyzers
- Visual Studio Graphics Debuggers
- Unity プロファイラ
- Unity フレーム デバッガー
- Unreal インサイト
- PIX
- Unreal での GPU プロファイリング
任意の環境でプロファイリングする方法
アプリが GPU にバインドされているか CPU にバインドされているかを判断する 1 つの方法は、レンダー ターゲットの出力の解像度を下げることです。 計算するピクセル数を減らすことで、GPU の負荷を減らすことができます。 デバイスでは小さなテクスチャがレンダリングされた後、アップサンプリングされて最終的な画像が表示されます。
レンダリングの解像度を下げた後の結果を次のように確認します。
- アプリケーションのフレーム レートが上がった場合は、GPU バインドの可能性があります
- アプリケーションのフレーム レートが変わらない場合は、CPU バインドの可能性があります
Note
Unity では、XRSettings.renderViewportScale プロパティを使用することで、ランタイムにアプリケーションのレンダー ターゲットの解像度を簡単に変更することができます。 デバイスに表示される最終的な画像の解像度は、固定されています。 プラットフォームでは、低解像度の出力がサンプリングされ、ディスプレイにレンダリングするための高解像度の画像が構築されます。
UnityEngine.XR.XRSettings.renderScale = 0.7f;
アプリケーションを改善する方法
CPU のパフォーマンスに関する推奨事項
一般的に、CPU で行われる Mixed Reality アプリケーションの処理のほとんどは、シーンの "シミュレーション" とアプリケーションのロジックの処理です。 最適化の対象となる領域は次のとおりです。
- アニメーション
- 物理計算
- メモリの割り当て
- 複雑なアルゴリズム (逆運動学、パスファインディングなど)
GPU のパフォーマンスに関する推奨事項
帯域幅とフィル レートについて理解する
GPU 上でフレームをレンダリングする際、アプリケーションはメモリ帯域幅またはフィル レートのいずれかの制限を受けます。
- メモリ帯域幅とは、GPU がメモリから読み書きできる速度のことです
- 帯域幅の制限を確認するには、テクスチャ品質を下げて、フレーム レートが改善されたかどうかを確認します。
- フィル レートの制限を特定するには、ディスプレイの解像度を下げて、フレームレートが向上するかどうかを確認します。
Note
Unity を使用している場合は、Unity 固有の GPU パフォーマンスに関する推奨事項をチェックします。 - Unity で XRSettings.renderViewportScale プロパティを使用する
メモリ帯域幅の最適化には、以下の要素が関わります。
- テクスチャの解像度を下げる
- 使用するテクスチャの数を減らす (法線、反射など)
フィル レートは、最終的にレンダリングされるピクセルに対して計算する必要のある操作の数を減らすことに重点を置いています。
- レンダリングおよび処理するオブジェクトの数
- シェーダーごとの操作数
- 最終 GPU ステージの数 (ジオメトリ シェーダー、後処理効果など)
- レンダリングするピクセル数 (画面の解像度)
多角形の数を減らす
多角形の数が多いと GPU の操作量が多くなるため、シーンのポリゴン数を減らすとレンダリング時間が短縮されます。 ジオメトリのシェーディングに負荷がかかる要因は他にもありますが、多角形の数はシーンのレンダリングにどれだけの作業が必要かを判断する最もシンプルなメトリックです。
オーバードローを制限する
オクルード オブジェクトに隠れて画面に表示されていない箇所で、複数のオブジェクトがレンダリングされていると、オーバードローが高くなります。 背後にオブジェクトがある壁を見ていると仮定します。 レンダリングする必要があるのは不透明な壁だけですが、すべてのジオメトリがレンダリング処理されるため、不要な処理が発生します。
シェーダー
シェーダーは、GPU 上で動作する小さなプログラムで、レンダリングにおける 2 つの重要な手順を実行します。
- 描画する頂点と、それが画面内のどこにあるかを決定します (頂点シェーダー)
- 頂点シェーダーは、すべてのメッシュの頂点ごとに実行されます。
- 各ピクセルの色を決定する (ピクセル シェーダー)
- ピクセル シェーダーは、ピクセルごとに実行され、ジオメトリによってターゲットのレンダリング テクスチャーにレンダリングされます。
通常、シェーダーは多くの変換や照明計算を実行します。 複雑な照明モデルや影などの演算を行えば優れた結果が得られますが、これには代償も伴います。 シェーダーでの計算の操作数を減らすことで、フレームあたりの GPU に必要な作業を大幅に減らすことができます。
シェーダーのコーディングに関する推奨事項
- 可能な限り、バイリニア フィルタリングを使用する
- MAD 組込み関数を使用して乗算と加算を同時に行うように式を組み替える
- CPU で可能な限り事前計算し、定数としてマテリアルに渡す
- ピクセル シェーダーから頂点シェーダーへの移動操作を優先する
- 一般的に、頂点の数はピクセルの数よりはるかに少なくなっています (720p のピクセル数は 921,600、1080p の場合は 2,073,600 ピクセルなど)。
GPU ステージを削除する
MSAA のようなアンチエイリアス技術などの後処理効果はコストがかかり、アプリケーションのフィル レートを増加させてしまいます。 HoloLens では、これらの技術や、ジオメトリ、ハル、コンピュート シェーダーなどのシェーダー ステージの追加は避けることを推奨しています。
メモリに関する推奨事項
メモリの割り当てと割り当て解除の操作が過剰に行われると、パフォーマンスが安定せず、フレームがフリーズしたり、その他の悪影響を引き起こす可能性があります。 メモリ管理はガベージ コレクターによって制御されるため、Unity で開発するときは、メモリに関する考慮事項を理解することが特に重要です。
オブジェクト プーリング
オブジェクト プーリングは、オブジェクトが繰り返し割り当てられたり割り当て解除されたりするコストを削減するための一般的な手法です。 これを行うには、同一のオブジェクトの大規模なプールを割り当て、時間の経過と共にオブジェクトを絶えず生成して破棄するのではなく、このプールの非アクティブで使用可能なインスタンスを再利用します。 オブジェクト プールは、アプリの間の有効期間が一定ではない再使用可能なコンポーネントに最適です。