プロファイリング
モバイルのための最適化実用ガイド

最適化

PC と同様に iOS および Android はさまざまなレベルのパフォーマンスを持ったデバイスがあります。ある電話より 10 倍のレンダリング処理能力を持つ電話が容易に見つかるくらいです。 スケーリングを行う簡単な方法としては:

  1. ベースラインの設定で問題なく実行できることを確認する
  2. 高いパフォーマンスが許容される設定では見映えをあげる:
    • 解像度
    • ポストプロセッシング
    • MSAA
    • Anisotropy(異方性)
    • シェーダー
    • Fx /パーティクル密度の有効/無効

GPU へのフォーカス

グラフィックスパフォーマンスはフィルレート、ピクセル数、およびジオメトリの複雑さ(頂点カウント)により制約されます。これら 3 つともレンダラーをカリングする方法がみつけられれば削減することができます。オクルージョンカリングがここでは役立つかもしれません。Unity は自動的にビュー Frustum の外にあるオブジェクトをカリングします。

モバイルではフィルレートの制約(フィルレート = 画面ピクセル数 x シェーダー複雑さ x オーバードロー)があり、複雑すぎるシェーダーがもっとも良くある問題です。このため Unity に付属するシェーダーを使用するか自身で設計するときはできるだけシンプルにします。もし可能であればピクセルシェーダーを頂点シェーダーに移行してシンプルにします。

Quality Settings で Texture Quality を下げることでゲームが速くなる場合メモリ帯域幅により制限されている可能性が高いです。テクスチャを圧縮する、ミップマップを使用する、テクスチャサイズを削減する、等々を行います。

LOD (Level of Detail) - によりオブジェクトをシンプルにするか遠ざけるにつれて完全に削除します。

グッドプラクティス

モバイル GPU は生成する熱量、使用する電力、大きさや音の大きさに大きな制限があります。このためデスクトップ部品と比較してモバイル GPU は帯域幅が少なく、ALU パフォーマンスが低く、テクスチャリングパワーが劣ります。GPU のアーキテクチャはできる限り小さい帯域幅で少量の電力を使用するように最適化されてます。

Unity は OpenGL ES 2.0 に最適化されていて、GLSL ES (HLSL と類似) シェーディング言語を使用します。ビルトインシェーダーはほとんど HLSL (Cg とも知られる) で書かれています。これはモバイルプラットフォームでは GLSL ES にクロスコンパイルされます。直接 GLSL を記述することもできますが、GLSL->HLSL の変換ツールが現在ないため OpenGL 関連のプラットフォームが制限されます(例えばモバイルおよび Mac )。HLSL で float/half/fixed 型を使用すると GLSL ES では highp/mediump/lowp 精度表現になります。

よい慣習とすべきことのチェックリストを次に示します:

  1. マテリアル数をできるだけ少なくします。これにより Unity のバッチングが容易になります。
  2. 個別テクスチャよりもテクスチャアトラス(サブ画像を含む大きな画像)を使用する。これらのほうがロードが高速であり、ステートの変更が少なく、バッチングと親和性があります。
  3. テクスチャアトラスや共有マテリアルを使用する場合 Renderer.sharedMaterialRenderer.material の代わりに使用します。
  4. Forward rendering ピクセルライトは負荷が高いです。
    • 可能なかぎりリアルタイムライトの代わりにライトマッピングを使用してください。
    • quality settings で pixel light count を調整します。要は指向性ライトのみがピクセル単位であるべきで、その他はすべて頂点単位とすべきです。これは当然にゲームによってどうすべきか決まります。
  5. Quality Settings の Render Mode of Lights で試行錯誤して正しい優先度となるようにします。
  6. カットアウト(アルファテスト)シェーダーは本当に必要な場合に限定します。
  7. 画面で使用する透明(アルファブレンド)を最小限にします。
  8. 複数のライトが特定のオブジェクトを照らす状況をあらかじめ避けます。
  9. シェーダーパスの全体的な数を削減します(シャドウ、ピクセルライト、反射)。
  10. レンダリングの順番は重要です。一般的には:
    • 完全に不透明なオブジェクトはおおむね手前から奥の順
    • アルファテストのオブジェクトはおおむね手前から奥の順
    • skybox.
    • アルファブレンドのオブジェクト(必要であれば奥から手前の順)
  11. モバイルでポストプロセッシングは負荷が高いため、慎重に使用します。
  12. パーティクル:オーバードローを削減し、できる限りシンプルなシェーダーを使用します。
  13. メッシュのダブルバッファが毎フレーム更新されます:
void Update (){
  // flip between meshes
  bufferMesh = on ? meshA : meshB;
  on = !on;
  bufferMesh.vertices = vertices; // modification to mesh
  meshFilter.sharedMesh = bufferMesh;
}

シェーダーの最適化

フィルレートにより制限されているか判断するのは容易です: 画面解像度を下げるとゲームは速くなりますか? もしそうであれば、フィルレートにより制限されています。

次の手法によってシェーダーの複雑さを軽減します:

  • アルファテストのシェーダーを避けます。代わりにアルファブレンドの種類を使用します。
  • シンプルで最適化されたシェーダーのコードを使用します( Unity 出荷時の Mobile シェーダーと同様に)。
  • シェーダーで負荷の高い math 関数の使用を避けます (pow、exp、log、cos、sin、tan、その他)。代わりに事前計算されたルックアップテクスチャを使用することを考えてください。
  • できる限り低い精度の型を(float, half, fixedin Cg) 使用してベストのパフォーマンスを得ます。

CPU へのフォーカス

ゲームが GPU のピクセル処理により制限されてる場合が良くあります。結果的に、特にマルチコアモバイル CPU で、未使用の CPU 処理能力が余ることになります。このため GPU 作業を代わりに CPU に付け替えることが効果的です( Unity はこれをすべて自動的に行います):メッシュスキニング、小さいオブジェクトのバッチング、パーティクルオブジェクトの更新など。

これらはやみくもに行うのではなく、慎重に使用するべきです。ドローコールがネックではない場合、バッチングはカリングの効率を下げるだけでなく、ライトにより影響を受けるオブジェクト数を増やしてしまうので、パフォーマンスに悪影響を与えます。

グッドプラクティス

  • FindObjectsOfType (および Unity の getter プロパティー全般)は非常に遅いため慎重に使用してください。
  • 動かないオブジェクトの Static プロパティーを有効にして、静的バッチングなどの内部最適化を許可してください。
  • CPU サイクルを大量に消費してオクルージョンカリングおよび優れたソートを行ってください( Early Z-cull を活用するため)

物理計算

物理計算は重い CPU 処理を伴います。エディタープロファイラーによりプロファイリングできます。もし CPU 上で物理計算の時間がかかりすぎている場合:

  • Time.fixedDeltaTime (Project settings -> Time にて) を微調整してできるだけ大きくします。もしゲームが遅い動作であれば、クイックなアクションを求められるゲームより fixed update を少なくするのがよいでしょう。クイックなアクションを求められるゲームは計算がより頻繁に必要であり、結果的に fixedDeltaTime を、より小さな値としないとコリジョンが正しく動作しない可能性があります。
  • Physics.solverIterationCount (Physics Manager)。
  • できるかぎりクロスオブジェクトを少なく使用します。
  • Rigidbody の使用を必要な場合に限ります。
  • プリファレンスメッシュコライダーでプリミティブコライダーを使用します。
  • スタティックコライダー(すなわち Rigidbody のないコライダー)はパフォーマンス影響が甚大であるため絶対に動かさないこと。プロファイラーで Static Collider.Move と表示されますが実際の処理は Physics.Simulate にあります。もし必要であれば RigidBody を追加して isKinematic に true をセットします。
  • Windows 上で NVidia の AgPerfMon プロファイリングツールを使用して必要に応じてより詳細が得られます。

Android

GPU

これらはポピュラーなモバイルアーキテクチャです。これは PC/コンソールで両方とも異なるベンダーであり、通常の GPU と異なる GPU アーキテクチャです。

  • ImgTec PowerVR SGX - タイルベース、ディファード:すべてを小さなタイルとして(16x16 として) レンダリングし、表示されるピクセルのみシェードします。
  • NVIDIA Tegra - Classic: すべてをレンダリング
  • Qualcomm Adreno - タイル: すべてをタイルでレンダリングし、大きなタイルとして(256k として) 処理します。
  • ARM Mali Tiled: すべてをタイルとして(16x16 として) 処理し、レンダリングします。

異なるレンダリングのアプローチを検討してゲームをそれにあわせて設計します。ソートには特別に注目します。開発サイクルの早い段階でサポートする最低限のローエンドデバイスを決めます。ゲームを設計するときにプロファイラーを有効にしてテストします。

プラットフォーム固有のテクスチャ圧縮を使用します

さらなる詳細

画面解像度

Android バージョン

iOS

GPU

PowerVR アーキテクチャ(タイルベースディファード)のみを気にすればよいです。

  • ImgTec PowerVR SGX。タイルベース、ディファード: すべてをタイルとしてレンダリングし、表示されるピクセルのみシェードします。

つまり:

  • ミップマップはそれほど必要ではありません。
  • アンチエイリアスおよび Aniso (異方性)は十分安価で、iPad 3 では必要ない場合もあります。

また欠点として:

  • もしフレーム毎の頂点データ(頂点数×頂点シェーダー後の必要ストレージ)がドライバにより割り当てられた内部バッファを超えると、シーンは “分割” される必要がありパフォーマンスが消費されます。ドライバがそれ以降、さらに大きなバッファを割り当てるか、頂点カウントを減らす必要があるかもしれません。これは iPad 2 (iOS 4.3) において 10 万頂点でかなり複雑なシェーダーにおいて顕著となります。
  • タイルベースディファードレンダリング( TBDR )はより多くのトランジスタをタイリングおよびディファード部分に割り当てる必要があり、概念的には “生のパフォーマンス” に割り当てられるトランジスタが少なくなります。TBDR で GPU タイミングを得ることは難しく、プロファイリングが難しくなります。

さらなる詳細

画面解像度

iOS バージョン

ダイナミックオブジェクト

アセットバンドル

  • アセットバンドルはある制限までデバイス上にキャッシュします
  • エディター API を使用して作成します
  • 読み込みに WWW API を使用します: WWW.LoadFromCacheOrDownload またはリソースとして AssetBundle.CreateFromMemory や AssetBundle.CreateFromFile を使用します
  • AssetBundle.Unload を使用してアンロードします。バンドルをアンロードし、それからロードアセットを維持するオプションがあります。また、シーンの中で参照している場合でも、ロードされたすべてのアセットをキルできます。
  • Resources.UnloadUnusedAssets はしばらく参照されなかったすべてのアセットをアンロードするので、必要のないアセットへの参照をキルするために覚えています。パブリックおよび静的変数は、ガベージコレクトされません。
  • Resources.UnloadAsset は、メモリから特定のアセットをアンロードします。必要に応じて、ディスクからリロードできます。

iOS 上でアセットバンドルの同時ダウンロード数の制限はありますか?(例えば、同時に(または毎フレーム) 10 個のアセットバンドルを安全にダウンロードできますか?)

ダウンロードは OS により提供される非同期 API により実装されているため、OS がいくつのスレッドがダウンロードで作成する必要があるか判断します。複数の同時ダウンロードを起動するとき、デバイスがサポートできる帯域幅の合計および空きメモリ量を頭に入れる必要があります。各々の同時ダウンロードにより一時バッファが割りあてられるため、メモリが不足しないように注意すべきです。

リソース

  • アセットは、ビルドに置かれる Unity によって認識される必要があります。
  • Unity がバイナリデータとして認識できるように任意の生のバイトファイルにファイル拡張子 .bytes を追加します。
  • Unity がテキストアセットとして認識できるように、任意のテキストフ​​ァイルにファイル拡張子 .txt を追加します。
  • リソースはビルド時にプラットフォームの形式に変換されます。
  • Resources.Load()

問題点のチェックリスト

  • テクスチャを適切に圧縮しない
  • 異なるケースのためのさまざまなソリューション、しかし、してはいけないことを確認している場合を除き、テクスチャを圧縮するようにしてください。
  • ETC/RGBA16 - android のデフォルトは、GPU ベンダーに依存して調整することができます。最善のアプローチは、可能な限り ETC を使用することです。アルファテクスチャはアルファのための 1 チャネルと 2 つの ETC ファイルを使用できます。
  • PVRTC - たいていの場合によい iOS 用のデフォルト値
  • テクスチャのピクセル Get/Set を有効にする - フットプリントが倍増し、Get/Set しない限り、チェックを外す必要があります。
  • テクスチャをランタイム上の JPEG/PNG 画像からロードすると非圧縮になります。
  • 大きいサイズの mp3 ファイルはロードする時に圧縮率が少ない状態でマークされます。
  • シーンを追加でローディング
  • 未使用のアセットはクリアされずにメモリ内に残ります。
  • まれにクラッシュした時は、開発キットまたは( iPad の 3 のように) 2 GB のメモリを搭載したデバイス上で試してみてください。

まれにクラッシュした時は、コンソールには何もありません。

  • 高速なスクリプト呼び出しとストリッピングは、iOS 上のランダムなクラッシュを引き起こすことがあります。無くして試してみてください。
プロファイリング
モバイルのための最適化実用ガイド