Unity を検索

Unity と Arm を使用してモバイルゲームのプロファイリングに対処する

2021年3月11日 カテゴリ: ゲーム | 11 分 で読めます
Game character
Game character
取り上げているトピック
シェア

Is this article helpful for you?

Thank you for your feedback!

Unity と Arm が提供するプロファイリングツールを使用して、モバイルのパフォーマンスの問題に対処する方法について説明します。Unity を使用してプロファイリングを行う方法、パフォーマンスの低下を最適化する方法、およびゲームアセットを最大限活用するためのヒントやコツを掘り下げます。

このブログでは、Unity と Arm が提供するプロファイリングツールを活用して、モバイルゲームのパフォーマンスの問題を特定する方法について検討します。また、モバイルゲームのコンテンツを最適化するためのベストプラクティスを紹介します。 

ゲーム内のパフォーマンスの問題を特定するには、まずはさまざまなデバイスでテストしてみてください。これを行う一番良い方法は、実際のデバイスでパフォーマンスのプロファイルをキャプチャーすることです。Unity プロファイラーフレームデバッガーなどのツールは、ゲーム内の要素がリソースを消費している場所を詳しく分析するのに役立ちます。さらに、Arm Mobile Studio などのツールを使用すると、デバイスからパフォーマンスカウンターのアクティビティデータをキャプチャーできるため、CPU リソースと GPU リソースのゲーム内での使用状況を正確に把握できます。私たちが使用したデバイスには Mali GPU が搭載されていますが、ここで紹介している考え方は他のモバイル GPU にも当てはまります。

テストの例

私たちがテストするのは、襲いかかってくる敵 NPC の群れにプレイヤーが近接攻撃や魔法攻撃で対峙する、アクション RPG です。この種のゲームは、画面上にキャラクターがどんどん増えていくほか、複数のパーティクルやポストプロセッシングのビジュアルエフェクトにより、モバイルデバイス上ですぐに GPU バウンドになってしまいます。 

Unity プロファイラーとフレームデバッガーを使用したプロファイリング

私たちはパフォーマンスが低下している部分がないかを特定するために、ゲーム全体に対して Unity プロファイラーを実行しました。優先して対処する必要がある疑わしい動作として、ポストプロセッシング、固定時間ステップ、インスタンス化によるスパイクが見つかりました。

ポストプロセッシング

ゲームの CPU のパフォーマンスが損なわれている主な原因は、ポストプロセッシングエフェクトでした。 

Render Camera is taking a huge amount of time and crosses the frame boundary
Render Camera に時間を大きく取られており、フレームの境界を越えている

すべてのポストプロセッシングエフェクトのうち、シーン内の明るい領域を光らせる、ブルームパスのコストが最も高くなっていました。

上のスクリーンショットから、Render Camera に時間を大きく取られており、フレームの境界を越えていることがわかります。その後、メインスレッドは次のフレームを準備する前に、レンダリングコマンドが完了するまで待機します。何が起こっているかを Unity フレームデバッガーで確認してみましょう。

Viewing the Frame Debugger with device at full screen resolution
フルスクリーン解像度のデバイスでフレームデバッガーを表示する

フレームデバッガーを見て最初に気付くことは、ゲームがデバイスのフルスクリーン解像度でレンダリングされていることです。平均的なモバイル端末の場合、コンテンツの複雑さを考慮すると、これによってデバイスの GPU に過度な負荷がかかります。解像度をより手頃な 1080p や 720p にまで下げることで、ゲームのレンダリング(特にポストプロセッシングエフェクト)にかかるコストが大幅に下がります。

Viewing the draw calls for the bloom pyramid
ブルームピラミッドのドローコールを表示する

次に観測するポイントは、ブルームピラミッドのブルームエフェクトが 25 個のドローコールで発生しているという点です。各ドローコールは 1 つのターゲットバッファーを表し、サイズはデバイスのフルスクリーン解像度の半分の解像度で始まります。この解像度はその後、イテレーションごとに半分になります。見込まれるイテレーションの数を減らす方法の 1 つとして、最初のレンダリング解像度を下げる方法が挙げられます。もう 1 つの代替案として、ブルームエフェクトのソースコードを変更して発生するイテレーションの数を減らし、いくつかの理にかなった制限を課す方法が挙げられます。ただし、それらのエフェクトを処理するのにかかる膨大な時間を考慮すると、このケースでは当面のところはポストプロセッシングエフェクトを無効にするほうが良いでしょう。当面のところとは、少なくともゲームの残りの部分を 30 fps でスムーズに実行できるようになるまでです。 

固定時間ステップ

プロジェクト改善するもう 1 つの方法は、固定時間ステップの間隔の周波数を減らすことです。現在は 1 フレームに複数回呼び出される程度に間隔が短くなっていることがわかります。Unity のデフォルトでは、0.02 つまり 50Hz に設定されます。30 fps を目指しているモバイルのタイトルの場合は、固定時間ステップの値を 0.04 に設定してみてください。この値に設定する理由として、0.333 に設定すると 30 fps にはなりますが、1 フレームで時間がスパイクする可能性があり、そうなると次のフレームで 2 回の呼び出しが発生してしまいます。これが意味することは、より時間がかかってしまい、フレームがわずかに長くなるサイクルは決して解消できないということです。遅れを取り戻すことに目標とする以上の時間をかけないようにするために、許可される最大時間ステップを設定することもできます。 

この時間ステップの継続時間は、FixedUpdate 関数を使用しているスクリプトや、固定された更新間隔で更新される Unity 内部のシステム(物理演算、アニメーションなど)に影響を及ぼします。

Timeline using a fixed Timestep
固定時間ステップを使用している Timeline

このプロジェクトにおいて、かかる時間に大きく影響するのは物理演算と Cinemachine のみで、1 回の呼び出しにつき、すなわちシステムが完全に更新されるまでにかかる時間は 3 ミリ秒ほどです(とはいえ、追加で 5 回呼び出されると、1 フレームにつき最大で 15 ミリ秒の時間が余計にかかることがあります)。

FixedUpdate.PhysicsFixedUpdate slowed by post-processing effects
ポストプロセッシングエフェクトにより低速になった FixedUpdate.PhysicsFixedUpdate

これは、低速なポストプロセッシングエフェクトが原因で発生します。オフにすると消費される時間は減りますが、CPU の不要な処理を回避するうえで、前に推奨した固定時間ステップの周波数を減らすことは依然として有効です。 

インスタンス化によるスパイク

プロファイリング中、フレーム時間にスパイクが見られることがあります。CPU プロファイラーの階層ビューで追跡してみると、NPC のインスタンス化が原因であることがわかります。 

Tracking instantiation spikes in the CPU profiler hierarchy
CPU プロファイラーの階層でインスタンス化によるスパイクを追跡する

最も一般的な解決方法は、キャラクターを事前にインスタンス化し、何らかのオブジェクトプール内で待機状態のままにしておく方法です。こうすることで、インスタンス化によるコストをかけることなく、プールから NPC を取得できます。さらに必要な場合は、必要に応じてプールを拡張できます。 

アビリティが使用されるときにも同じ問題が見られます。これもオブジェクトがインスタンス化されていることが原因です。  

CPU Instantiation spikes
インスタンス化による CPU のスパイク

これらの問題を解決するには、オブジェクトプールを作成するのが最も簡単な方法です。ロード時間に影響する可能性はありますが、ランタイムのフレームレートがはるかにスムーズになります。このケースでは、どちらの方がまだましであるかということです。 

Arm Mobile Studio を使用したプロファイリング

私たちはゲームの動作をより詳しく分析するために、Arm Mobile Studio も使用しました。Mobile Studio のツールを使用すると、CPU や GPU のパフォーマンスカウンターのアクティビティデータを入手できるため、ゲームがデバイスのリソースをどのように使用しているかを正確に把握できます。 

Arm Mobile Studio はこちらから無料でダウンロードできます。次の 4 つのツールが搭載されています。

  • Performance Advisor – 読みやすいレポートを生成し、最適化するためのアドバイスが得られます
  • Streamline – すべてのカウンターアクティビティをキャプチャーする包括的なパフォーマンスプロファイラー
  • Mali Offline Compiler – Mali GPU 上でのシェーダープログラムのパフォーマンスをチェックします
  • Graphics Analyzer – グラフィックス API の呼び出しをデバッグし、コンテンツがどのようにレンダリングされたかを解析します

Performance Advisor

Performance Advisor はゲームのパフォーマンスに関する簡単なサマリーを提供し、定期的な正常性チェックとして使用することを意図しています。特にお使いのナイトリービルドシステムと一緒に継続的インテグレーションワークフローに組み込むと、レポートの生成が迅速になります。 Performance Advisor はゲームのパフォーマンスに関する簡単なサマリーを提供し、定期的な正常性チェックとして使用することを意図しています。特にお使いのナイトリービルドシステムと一緒に継続的インテグレーションワークフローに組み込むと、レポートの生成が迅速になります。 

Performance Advisor - Capture Summary
Performance Advisor - Capture summary(キャプチャーのサマリー)

Performance Advisor によると、ゲームの最初の 2 分間で平均して 17 fps しか出ていません。Frame rate analysis(フレームレート分析)グラフの最初の緑のセクションはゲームのロード中を示しています。その後突然、グラフがゲームがフラグメントバウンドになったことを示す青に変わり、ずっとその状態が続きます。これは、デバイスの GPU がフラグメントワークロードの処理に追われていることを意味し、ゲームが要求している処理量が多すぎるか、ピクセルを効率的に処理していないことを示唆しています。 

ゲームにはリージョンの注釈を追加しているため、Frame rate analysis(フレームレート分析)グラフには私たちが定義したリージョン名が表示されています。グラフに「S」というマーカーが表示されている部分は、その時点の画面で何が発生しているのかを特定するのに役立つよう、Performance Advisor によってゲームのスクリーンショットが撮影された部分です。fps が指定した値を下回った場合に画面がキャプチャーされるように設定できます。ここでは、fps が全体を通して低い水準にあるため、Performance Advisor はデフォルトの 200 フレームごとの間隔でスクリーンショットをキャプチャーします。

GPU cycles per frame(フレームごとの GPU サイクル)のグラフを見てみましょう。ここでは、このデバイスの予算を、1 フレームにつき 2,800 万サイクルに設定しています。私たちはこの数値が、このデバイスが 30 fps のフレームレートを維持しつつ、処理できるサイクルの最大数であると見積もりました。ここでは、GPU サイクルの数がこの予算を大幅に上回っており、そのサイクルの数が経時的に増えていることを確認できます。

Performance Advisor - GPU cycles per frame
Performance Advisor - GPU cycles per frame(フレームごとの GPU サイクル)

問題が見つかったら、Performance Advisor によって最適化するためのアドバイスが提供されます。Shader cycles per frame(フレームごとのシェーダーサイクル)グラフを見てみると、実行エンジンのサイクル数が高いことがわかります。Mali シェーダーコアの内部では、算術演算の処理を実行エンジンが担います。Performance Advisor によってこれが問題としてフラグされ、シェーダーの演算を減らすようアドバイスが提供されます。

Performance Advisor - Shader cycles per frame
Performance Advisor - Shader cycles per frame(シェーダーごとの GPU サイクル)

これは簡単に修正できます。シェーダー変数の精度を highp から mediump に減らしても、画面上に目立った変化はありません。これにより、シェーダーのコストが大幅に削減されます。これを行う方法の詳細については、Unity ドキュメンテーションの「シェーダーのデータ型と精度」を参照してください。さらに、以前に Unity のフレームデバッガーで発見したように、現在はゲームがデバイスのフルスクリーン解像度でレンダリングされています。ゲームのレンダリング解像度を(1080p または 720p に)減らすために適用したさまざまな変更により、フラグメントのシェーディングコストも削減されます。 

コンテンツに関わる指標

私たちはこのデバイスの頂点数の予算を、各フレームにつき 500,000 個に設定しています。頂点数は約 45 秒でその予算を超過し、経時的に着実に増加しています。

Performance Advisor - Vertices per frame
Performance Advisor - Vertices per frame(フレームごとの頂点数)

Primitives per frame(フレームごとのプリミティブ数)グラフを見てみると、可視のプリミティブ数は比較的一定であるにもかかわらず、処理されるプリミティブの合計数が経時的に増加していることがわかります。ゲームの最初の 2 分間に作成される新しいオブジェクトは敵 NPC のみで、その後主人公が放つ雷に撃たれて破壊されます。これが示唆することは、敵が破壊されると、それらの敵のジオメトリは見えなくなるものの、そのまま残るということです。

Performance Advisor - Primitives per frame
Performance Advisor - Primitives per frame(フレームごとのプリミティブ数)

GPU がゲームの需要を処理できないことがある理由はいくつかあるため、Streamline を使用して Arm のプロファイリングツールをいろいろと調べる必要があります。Streamline を使用すると、このフラグメントの重いワークロードの原因についてより詳しい情報が得られるほか、その他のカウンターを確認することで、負荷を軽くする方法について手がかりを見つけることができます。

Streamline による分析

ゲームの同じセクションを Streamline で見てみることで、各種グラフを確認して、ジオメトリ処理とピクセル処理の異なる段階における GPU カウンターのアクティビティを調べることができます。これは、ゲームのコンテンツが GPU によってどのように処理されているか、および不要なプロセスがないかどうかを示します。

Mali ベースの GPU は、グラフィックス関連のワークロードを処理するのに、スクリーンスペースが複数のタイルに分割され、各タイルが完了に向けて順番に処理される、タイルベースのアプローチを取ります。各タイルにつき、先にジオメトリ処理が実行され、その後ピクセル処理中にピクセルが色付けされます。

Mali-based GPUs processing graphics workloads
Mali ベースの GPU によるグラフィックス関連のワークロードの処理

フラグメントのワークロードによってデバイスの GPU がピークに達していることはすでにわかっているため、ピクセル処理の段階で負荷を減らす方法を探す必要があります。

ピクセル処理の負荷を減らす方法の 1 つは、まず第一にピクセル処理に送信されるジオメトリの複雑さを下げることです。完全に画面外にあるジオメトリや隠面はピクセル処理の前に除外されますが、2x2 のピクセルのクアッドを部分的にのみ覆う小さな三角形によりフラグメントの効率性が損なわれ、出力ピクセルごとの帯域幅のコストが高くなる可能性があります。 

Streamline にある Mali Geometry Usage(Mali のジオメトリの使用状況)グラフとMali Geometry Culling Rate(Mali のジオメトリのカリングレート)グラフは、GPU によるジオメトリ処理の効率性を示します。GPU に送信されているプリミティブの数と、そのうちジオメトリ処理中にカリングされている数を確認できます。この段階でカリングされている作業は、ピクセル処理を通ることはありません。これは良い知らせですが、不可視のプリミティブがその処理をまったく通ることがないように、コンテンツをより効率的に整理できます。

Mali Geometry Usage and Mali Geometry Culling Rate charts in Streamline
Streamline の Mali Geometry Usage(Mali のジオメトリの使用状況)グラフと Mali Geometry Culling Rate(Mali のジオメトリのカリングレート)グラフ

Mali Geometry Usage(Mali のジオメトリの使用状況)グラフでは、選択した時間枠(約 0.05 秒)で 107 万個のプリミティブがジオメトリ処理に入る(オレンジの線)ものの、70 万個のプリミティブがこの段階でカリングされます(赤い線)。

Mali Geometry Culling Rate(Mali のジオメトリのカリングレート)グラフは、カリングされた理由を示します。予想どおり、これらは 3D オブジェクトの隠面の三角形であるため、約半分が背面テストによってカリングされています(オレンジの線)。より懸念されるのは、サンプルテストによって 31.9% のプリミティブがカリングされたことです(紫の線)。理想としては、この数字は 5% を下回っている必要があります。これらのプリミティブはラスタライズされるには小さすぎるため、単一のサンプルポイントにはヒットせず、そのため不可視と見なされたことをサンプルテストは示しています。これは、複雑なメッシュを持つオブジェクトがカメラから離れたところに配置され、メッシュ内の三角形が小さすぎて見えないときに発生します。数字が大きい場合、そのゲームオブジェクトのメッシュが画面上の位置に対して複雑すぎることを示している可能性があります。  

サンプルテストを合格するのに十分な大きさであるものの、数ピクセルのみを覆うプリミティブの場合、この問題は悪化します。これらの「極小の三角形」はピクセル処理を通り、処理するにはコストがかかります。これは、フラグメントのシェーディング中に、三角形が 2x2 ピクセルのパッチ(クアッド)にラスタライズされるためです。小さな三角形は、クアッド内のピクセルのサブセットにしかヒットしないものの、処理のためにクアッド全体が送信される必要があります。 これはつまり、フラグメントシェーダーがハードウェア内の待機中のレーンで実行され、シェーダーの実行の効率性が落ちることを意味します。

Tiny triangles hitting a subset of pixels inside a quad
クアッド内のピクセルのサブセットにヒットしている小さな三角形

極小の三角形に関して問題があるかどうかをチェックするために、Streamline の Mali Core Workload Property(Mali のコアワークロードプロパティ)グラフを使用して、カバレッジの効率性を監視できます。理想としては、これは 10% を下回っている必要があります。ここでは、一部のセクションでカバレッジが部分的である比率(緑の線)が非常に高く、70% を超えていることがわかります。この値が示唆することは、コンテンツの極小の三角形の密度が高く、サンプルのカリングレートが高いことで以前にフラグが立てられた問題が裏付けられることです。

Mali Core Workload Property chart
Mali Core Workload Property(Mali のコアワークロードプロパティ)グラフ

画面上に表示されるジオメトリは、その位置に対して適切なサイズに設定する必要があります。遠く離れた場所にある複雑な風景は、シーンにあまり影響がないため、それほど細部まで描写しなくても問題ありません。カメラから遠く離れているオブジェクトには、詳細レベル(LOD)メッシュを使用して複雑さを減らし、DRAM 帯域幅の処理能力を節約できます。または、ジオメトリの代わりに、テクスチャーや法線マップを使用してオブジェクトの表面のディテールを制作できます。 

シェーダーの解析

Performance Advisor のレポートから、シェーダーのコストが高すぎるため、その精度を下げることが効果的であることがわかりました。Streamline で、Mali Varying Usage(Mali の可変ユニットの使用率)グラフを使用して、32 ビット(高精度)補間または 16 ビット(中精度)補間がアクティブになっているサイクルの数を確認できます。ここでは、32 ビット補間がほとんどのサイクルで使用されていることがわかります。16 ビットの変数による補間は 32 ビットの変数による補間の 2 倍高速であり、補間の結果を格納するためにシェーダーレジスターで使用されるスペースは半分で済むため、フラグメントシェーダーにはできるだけ mediump(16 ビット)の可変入力を使用することをお勧めします。

Mali Varying Usage chart
Mali Varying Usage(Mali の可変ユニットの使用率)

Mali Offline Compiler によるシェーダーの解析

シェーダーについて詳しく調べるために、Arm Mobile Studio の静的なオフラインコンパイラーツールを使用して、シェーダープログラムの簡単な解析を生成できます。

これを行うには、Unity から提供されるコンパイル済みのファイルからシェーダーコードを取得し、そのファイルに対して Mali Offline Compiler を実行する必要があります。

  1. Unity で、アセットのフォルダーから直接選択するか、マテリアルを選択し、歯車アイコンをクリックして「Select shader」を選択することで、解析するシェーダーを選択します。
  2. インスペクターで「Compile and show code」を選択します。コンパイル済みのシェーダーコードがデフォルトのコードエディターで開きます。このファイルにはいくつかのシェーダーコードのバリアントが含まれます。 
  3. 頂点シェーダーバリアントまたはフラグメントシェーダーバリアントのいずれかをこのファイルから新しいファイルにコピーし、拡張子を .vert または .frag に指定します。頂点シェーダーは #ifdef VERTEX で始まり、フラグメントシェーダーは #ifdef FRAGMENT で始まります。それらはそれぞれ対応する #endif で終了します。(新しいファイルには #ifdef と #endif どちらのステートメントも含めないでください)。
  4. コマンドターミナルで、このファイルに対して Mali Offline Compiler を実行し、テストする GPU を指定します。例: malioc –c Mali-G72 myshader.frag 手順の詳細については、Get started with Mali Offline Compiler(Mali Offline Compiler の使用を開始する)を参照してください。

私たちは敵 NPC が倒されたときに発生するディゾルブエフェクトを担っていた、フラグメントシェーダーを解析することにしました。こちらが Mali Offline Compiler のレポートです。注目する部分がハイライトされています。

Mali Offline Compiler report
Mali Offline Compiler のレポート

16 ビットの精度で効果的に行われていた算術演算はわずか 2% であることがわかります。精度を highp から mediump に減らせば、シェーダーはより効率的に動作します。これにより、エネルギー消費とレジスターの負荷の両方が減り、パフォーマンスを倍にすることができます。位置や深度の計算など、highp が常に必要な状況は存在しますが、多くのケースでは精度を mediump に下げても画面上で目立った違いはほとんど見つかりません。

レポートでは、Mali シェーダーコアの主な機能ユニットのサイクルコストのおおよその内訳を確認できます。ここでは、最も使用率が高いのは算術ユニットであることがわかります。

「Shader properties(シェーダーのプロパティ)」のセクションを見てみると、このシェーダーにリテラル定数または均一な値にのみ依存する均一な演算が含まれていることがわかります。これにより、ドローコールまたはコンピュートディスパッチ内の各スレッドで、同じ結果が生み出されます。理想としては、この種の均一な演算は CPU のアプリケーションロジックに移動したほうがよいでしょう。

また、discard ステートメントを使用してフラグメントがアルファしきい値を下回るようにすることで、各ピクセルのどのサンプルポイントがフラグメントで覆われるかを決定するフラグメントのカバレッジマスクが、シェーダーによって変更される可能性があることもわかります。カバレッジを変更可能なシェーダーは後期 ZS 更新を使用する必要があります。これにより、同じ座標での早期 ZS テストと後のフラグメントのためのフラグメントのスケジューリングの効率性が下がることがあります。フラグメントシェーダーでの discard ステートメントとアルファトゥカバレッジの使用は、できるだけ最小限に抑えるようにしてください。discard ステートメントの使用に関するアドバイスについては、Arm Mali のベストプラクティスガイドを参照してください。

Graphics Analyzer による Graphics API の呼び出しの解析

 

Arm Mobile Studio の Graphics Analyzer では、アプリケーションによって作成されたすべての Graphics API の呼び出しを確認し、それらを 1 つずつステップ実行して、シーンがどのように構築されているかを確認できます。これは、画面上のサイズやカメラからの距離に対して、複雑すぎるオブジェクトを特定するのに役立ちます。このゲームで見つかった例をいくつか紹介します。

シーンの隅にあるれんが造りの壁はジオメトリを使用して構築されており、2064 個の頂点が使用されています。最終的な出力ではそのディテールはあまり見えないため、これは無駄な処理になります。 

Brickwork sample at 2064 vertices
2064 個の頂点が使用されたれんが造りの壁のサンプル

床のタイルにも同じ問題が見つかりました。各タイルはそれぞれ 1170 個の頂点がありますが、オブジェクトがカメラから近いにもかかわらず、この複雑さがシーンにそれほどプラスの効果をもたらしていません。ここでは、凸凹や角ばったエッジを表現するのに、三角形を使用して構築するよりも、法線マップを使用するほうがより効率的になります。さらに、これらのオブジェクトは別個のドローコールを使用して描画されていることがわかります。複数のオブジェクトをバッチ処理してドローコールの数を減らすことや、オブジェクトインスタンシングを使用することで、パフォーマンスを高めることができます。

Floor tile sample at 1170 vertices
1170 個の頂点が使用された床のタイルのサンプル

もう 1 つの例は、シーンの後ろのほうにある立像です。それぞれ 6966 個の頂点が使用されています。メッシュが非常に複雑であるため、プレイヤーが立像に近づいたときのビジュアルは素晴らしいのですが、このカメラ位置からではほとんど目立ちません。ここでは、カメラからこれだけ離れているときのこれらのオブジェクトの表現にメッシュの LOD を使用すると、処理能力を大幅に節約できます。

Statue sample at 6966 vertices
6966 個の頂点が使用された立像のサンプル

大量にある類似のオブジェクトの複雑さを減らすことの積み重ねがジオメトリ処理の大幅な節約につながり、その後に必要なフラグメントのシェーディングの量を減らすことができることを覚えておいてください。これは、フラグメントのワークロードを引き下げ、毎秒フレームを増やすだけでなく、APK のインストールフットプリントを減らすことにもつながります。 

ゲームの最適化

ゲームのパフォーマンスを改善するために変更を加えることができるいくつかの領域を明らかにしました。こちらに私たちが実装することを選択した部分と、どのようにして実装したかを紹介します。

固定時間ステップ

固定時間ステップはフレームレートから独立した間隔で、物理演算や FixedUpdate() イベントが実行されるタイミングを制御します。デフォルトでは、これは 50 fps で実行されるよう設定されています。ハイエンドのモバイル端末では 50 fps か 60 fps でも持続可能である一方、より幅広く出回っているデバイスは 30 fps で実行されるので、このタイトルではそこを目標にします。「Edit > Project Settings」に移動し、「Time」カテゴリで、「Fixed Timestep」プロパティを 0.04 に設定します。これで物理演算、FixedUpdate()、更新がすべて同期的に実行されるようになります。 

Setting Fixed Timestep to 0.04 in Project Settings
「Project Settings」で「Fixed Timestep」を 0.04 に設定する

Unity の固定時間ステップに調整を加えた後は、メインのゲームループの固定された更新の部分は 1 フレームにつき 1 回のみ呼び出され、かかる時間は平均して 1.5 ミリ秒でした。以前は 12 ミリ秒かかっていたため、これは大幅な改善であり、一般的なパフォーマンスの落とし穴に対する簡単な解決法です。

Resources フォルダー

アプリの起動時に、ビルトインのシーンによって参照される、または Resources フォルダー内のすべてのオブジェクトのデータが、インスタンス ID のキャッシュにロードされます。これらのアセットは 1 つの大きなアセットバンドルとして扱われるため、メモリに常にロードされるメタデータやインデックス情報が存在します。このバンドルからのアセットは一度使用されると、メモリからアンロードされなくなります。 

メモリ消費量の改善を目指しているときに推奨されるアセットやリソースの処理方法は、Addressable Asset System を使用する方法です。これにより、メモリから必要とされなくなったコンテンツを効果的にアンロードできます。

GPU インスタンシング

ここで取り扱っている環境では、複数回出現するオブジェクトが数多く存在します。壁、床のタイルなど、環境で使用されている小道具がすべて複製され、このシーンを作り出しています。オブジェクトのマテリアルで GPU インスタンシングを有効にすることで、ドローコールを抑えることができます。GPU インスタンシングを使用すると、同一のメッシュを少数のドローコールでレンダリングし、各インスタンスに異なるパラメーター(色、スケールなど)を設定できます。この変更により、CPU のパフォーマンスを高めることができます。以下に示すのは、GPU インスタンシングを有効にする前の Performance Advisor のデータです。 

Data before GPU instancing is enabled
GPU インスタンシングを有効にする前のデータ

そしてこちらが、GPU インスタンシングを有効にしたアプリケーションの同じ部分です。目標とする 30 fps に向けて少しですがある程度の増加が見られます。 

Data with GPU instancing enabled
GPU インスタンシングを有効にしたデータ

レンダーテクスチャー

レンダーテクスチャーは、UI に 3D 要素を追加するなど、さまざまな用途で使用できます。レンダーテクスチャーにカメラレンダリングを設定している場合、画面外にあるときは必ずカメラを無効にしてください。ユーザーに表示されないものをレンダリングする必要はありません。Graphics Analyzer または Unity のフレームデバッガーを使用して、それらのテクスチャーがオフスクリーンで更新されていないことを確認してください。 

オブジェクトプーリング

同じオブジェクトの作成と破壊を繰り返すことで CPU に余分な作業をさせる代わりに、オブジェクトプーリングを試してみてください。オブジェクトプーリングは、必要になるオブジェクトを事前に作成するよう促すデザインパターンで、CPU の作業を前倒しします。その後、それらを破壊する代わりにプールに戻し、同じタイプのオブジェクトがまた必要になったときに再利用します。これは CPU の処理能力を緩和するのに優れた方法で、CPU の処理をゲームのより重要なタスクに柔軟に割り当てることができます。

オブジェクトプーリングへの移行により、画面上に敵が相次いで出現しても Unity プロファイラーのキャプチャーで特定可能なスパイクは発生せず、フレームレートにも目立った影響は見られません。

詳細レベル(LOD)メッシュ

画面上にメッシュがあると、サイズに関係なく、GPU はメッシュ内のすべての三角形をレンダリングするのに時間をかけます。カメラやアセットが移動することがあるゲームでは、これはしばしば、そのフレームでは小さすぎて見えないメッシュの三角形をレンダリングするのに、GPU の大量のリソースが消費されてしまう可能性がある状況につながります。これに対処するには、詳細レベル(LOD)メッシュを使用します。これにより、カメラがアセットから離れるにつれてより複雑さが少ないメッシュがゲームで利用されるようになり、GPU でレンダリングする必要があるメッシュの複雑さやフレームごとの頂点数が減ることで、より大きな三角形をピクセル処理に送ることができます。これにより、効率性が改善されるだけでなく、シーンの芸術性はそのまま保たれます。 

LOD Meshes
LOD メッシュ

その他のアセット最適化のヒントについては、Arm が提供するゲームアーティスト向けのガイドをご覧ください。  

テクスチャーアトラス

同じマテリアルプロパティを持つアセットが同じシーンで使用されることがわかっている場合は、それらを一括で処理できます。それらのテクスチャーデータを単一のテクスチャーアトラスに結合することで、描画を 1 回にしてドローコールを抑え、結果として複数の別個のファイルと比較して、圧縮されたときのフットプリントが小さくなります。

シェーダーの精度を「Float」にするか「Half」にするか

カスタムシェーダーを独自に記述するか、シェーダーグラフを使用するときは、使用する精度を「Float」または「Half」から選択できます。できる限り「Half」を選択することで、よりパフォーマンスの高いシェーダーになります。ただし、ワールド空間の位置や深度計算を扱う場合は、「Float」の使用が必要となる可能性が高くなることを覚えておいてください。

Setting Precision to Half in Shader Graph
シェーダーグラフで「Precision」を「Half」に設定する

統合された機能セットにするか、ポストプロセッシング V2 機能セットにするか

プロジェクトにポストプロセッシングエフェクトを適用する計画を立て始めると、2 つのオプション、つまり従来型の統合された機能セットと、新しいポストプロセッシング v2 機能セットのどちらを使用するかを選択する必要があります。以下に示すのは、統合された機能セットが使用されているゲームです。 

GPU Profiler without Post Processing v2
ポストプロセッシング v2 を使用しない GPU プロファイラー

3 から 4 フレームごとに V-Sync にスパイクが見られ、そのフレーム上でシステムがレンダリングを待機していました。これが原因でゲームが常に 30 fps を下回り、デバイスの処理能力が無駄に使用されています。ですが、こちらをご覧ください。同じエフェクトを使用しているゲームのプロファイラーデータで、今回はポストプロセッシング v2 機能セットを使用しています。 

GPU Profiler with Post Processing v2
ポストプロセッシング v2 を使用した GPU プロファイラー

このプロファイラーのグラフのほうがより良い結果が出ています。ポストプロセッシング v2 は、モバイルハードウェアで動作するように最適化されているからです。ポストプロセッシングのパフォーマンスを最大にするために、ご自身のプロジェクトで使用してみてください。 

ポストプロセッシングエフェクト

ゲームにポストプロセッシングエフェクトを追加すると、プロジェクトに美しい仕上げのレイヤーを追加してビジュアルに深みを持たせることができます。しかし、それらのエフェクトとパフォーマンスのバランスを取ることも重要です。結局のところ、そのようなエフェクトはコストが高くなってしまう可能性があるためです。広く市販されているデバイスでこれらのエフェクトをオフにすると、大量の処理が発生するのを回避し、プレイヤーの手の中でデバイスが発熱するのを抑えることができます。 

その他の最適化が適用された時点では、まだ一部の領域でスパイクが見られました。バイナリ検索を使用し、さまざまな要素をオンまたはオフにすることで、最終的に 2 つの要素を突き止めました。その 1 つが、使用されていたポストプロセッシングスタックでした。これは合計時間の改善には寄与しましたが、最終的にフレームレートが横ばいになったのは、アンチエイリアスをオフにしたときでした。この効果は非常に大きく、テストに使用していたスペックが最も低いデバイスでも、一部のポストプロセッシングをオンのままにすることができました。

GPU Profiler - optimized Example
GPU プロファイラー - 最適化された例

ゲームを最適化した後に、もう一度 Arm Mobile Studio を一通り実行し、違いを確認しました。Performance Advisor のレポートに、FPS が平均で 28.9(以前は 17)に到達し、全体的なフラグメントバウンドが減少していると示されるようになりました。ゲームの一部のセクションではフラグメントアクティビティがいまだに高いため、まだやることは残っていますが、調査を進めるための優れたデータの存在により、これらのセクションを最適化して、さらにパフォーマンスを改善できるはずです。

Arm Mobile Studio - Capture summary
Arm Mobile Studio - Capture summary(キャプチャーのサマリー)

1 フレームの頂点数が予算の 500,000 個以内に収まるようになり、敵の NPC が破壊される場面で正常な低下が見られます。

Arm Mobile Studio - Vertices per frame
Arm Mobile Studio - Vertices per frame(1 フレームの頂点数)

ジオメトリの使用状況とカリングがはるかに効率的になり、入力プリミティブ数に対する可視のプリミティブ数がはるかに健全になりました。背面テストは予想どおり、カリングされたプリミティブの 50% 程度の原因であり、サンプルテストによって除外されたものは 10% を下回り、非常に小さな三角形の数を減らしたことを示しています。

Mali Geometry Usage and Culling Rate
Mali Geometry Usage(Mali のジオメトリの使用状況)と Mali Geometry Culling Rate(Mali のジオメトリのカリングレート)

まとめ

Unity のプロファイラーとフレームデバッガーを Arm Mobile Studio と併用することで、パフォーマンスを改善し、モバイル端末の CPU と GPU の負荷を緩める複数の方法を見つけることができました。ここで明らかになったいくつかの問題は、コンテンツに関わる一連のベストプラクティスを守ることで、将来のタイトルでは回避できる可能性があります。

もちろん、最適化によって画面に表示されるビジュアルの質が下がることはあってほしくはありません。こちらで最適化されたバージョンの見た目が元のバージョンと比べてどのようになっているか確認してください。

先を見越したパフォーマンステスト

パフォーマンステストは、しばしば開発サイクルのかなり遅い段階で行われます。さらに最適化するチャンスが見つかるのは素晴らしいことですが、リリース間近で問題を修正する時間がない場合はどうすればよいでしょうか?最初から最適な形になるようコンテンツをデザインするほうがはるかに実用的です。モバイル向けに効率的にデザインする最も多くの機会をチームに与えるために、メッシュの複雑さ、シェーダーの複雑さ、テクスチャーの圧縮に関わる部分にコンテンツの予算を設定すると役立つことがあります。ここでチームを支援するリソースをいくつかご紹介します。

ほぼすべてのアプリケーションとアセットが一連のベストプラクティスに従っていることを確認した後は、修正が間に合う段階で問題を特定できるように、開発サイクルを通してパフォーマンステストを定期的に行うことができます。

継続的インテグレーションシステムを使用するチームは、Arm Mobile Studio Professional Edition で利用できる、自動化されたパフォーマンステストを活用できます。このエディションをデバイスファーム内の複数のデバイスにわたって実行することで、手動でプロファイリングを行う手間を省くことができます。報告されたデータを JSON に対応するデータベースに取り込むこともできます。こうすることで、視認性の高いダッシュボードやアラートを構築し、パフォーマンスが経時的にどのように変化するかを監視して、問題に迅速にフラグ付けできます。

プロファイリングツールを使ったことがない方

まずは Unity のビルトインのプロファイラーを使ってみてください。Unity ドキュメンテーションで、アプリケーションをプロファイリングする方法を確認してください。または、フレームデバッガーをお試しください。個々のフレームがどのように構築されているかを調べることができます。

Arm 開発者ウェブサイトから Arm Mobile Studio をダウンロード(無料)し、Performance AdvisorStreamlineMali Offline CompilerGraphics Analyzer のスターターガイドを確認して、すぐに使い始めることができます。

連絡する

Unity のプロファイラーとフレームデバッガーを使用したプロファイリングに関してサポートが必要な方は、Unity のフォーラムにお気軽に質問を投稿してください。

Mali デバイスや Arm Mobile Studio での作業中にさらにサポートが必要な場合は、Arm の Graphics and Gaming forum(グラフィックスおよびゲーミングフォーラム)にアクセスし、質問を投稿してください。Arm がサポートします。 

2021年3月11日 カテゴリ: ゲーム | 11 分 で読めます

Is this article helpful for you?

Thank you for your feedback!

取り上げているトピック