UDN
Search public documentation:

MemoryDebuggingJP
English Translation
中国翻译
한국어

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

メモリ デバッギング

ドキュメント概要: Unreal Engine 内のメモリ リークやメモリ肥大化をトラックする方法。

ドキュメントの変更ログ: Lina Song により作成および更新。

目次

メモリ デバッギング概観

このセクションは、メモリに関する問題―特に物理的なメモリのサイズにメモリ使用を制限してしまうコンソールのメモリ―にどのようにアプローチして処理していくかを理解する手助けをします。

メモリの問題は、計画外のメモリ量、巨大なアセット、コード バグなどの状況により、ゲーム開発中によく起こることです。

このドキュメントは、これらの状況の多くをどのようにして処理するかの基本的なアイデアを提供するものでありますが、詳細に関してはプラットフォーム固有の問題を参照してください。

メモリのデバッギング: 段階的方法

1. ゲームのロード

メモリ不足のためゲームをロードできない場合は、 レベル最適化ガイド のページを参照し、レベルの最適化の方法を調べてください。

一般的に、メモリ量の確立されたガイドラインを持つことは良い方法と言えるでしょう。例えば、いくつの静的メッシュを 1 つのレベル内で使用するか、または骨格メッシュ、アニメーション、サウンドなどの他のアセットを 1 つのレベル内でどれだけ使用するか、ということです。これは、何をするゲームか、また何に関するゲームかによって、著しく変化します。

2. ゲームを起動して観察

ゲームが起動したとしても、メモリ不足から完全に逃れられたというわけではありません。ゲームがどれだけのメモリを使用しているか、また使い過ぎを避けつつ、最大限にするにはどれぐらいが利用可能かということを知っている必要があります。

プラットフォームの提供者は、これをするツールを有しています。 以下 のプラットフォーム セクションを参照してください。

3. メモリ使用量の軽減 (メモリ不足を防ぐ)

メモリ不足が起こる場合は、それが何であるかを絞り込む必要があります。

以下は可能性としてあるものです。

  • 静的メッシュの多すぎるレベル。
  • 多すぎる投射物やパーティクルを作成する AI。
  • 多すぎるメモリを割り当てるコード。

基本的な理解

どれぐらいのメモリが、どこに使用されるべきかという質問に対する普遍的な答えはありません。

それは、あくまでもゲーム次第であり、ゲームがどこに使用すると決定するかによるのです。

従って現在のメモリ使用量がどれだけであるかを理解し、適切な量を発見する必要があります。

  • メモリ量: 何にメモリを使用するかを決めます。
  • すべてのアセットが最適化されていて、不必要な参照がないことを確認します。
  • ゲームが動き続けるのに十分な (フラグメンテーションのための) バッファがあることを確認します。

参考事例

           #define KEEP_TRACK_OF_NOVODEX_ALLOCATIONS 1

参考事例

レベル最適化

Stats

STAT LEVELS:

Stat levels を実行し、メモリでいくつのレベルが読み込まれているか確認してください。

  • 緑色: 読み込まれていない
  • 赤色: 読み込まれている (読み込みにかかった時間も表示)
  • 青色: 読み込まれておらず、ガーベジ コレクションされている
  • 黄色: 読み込まれているが非表示
  • ピンク色: 読み込まれる準備ができている

読み込まれる必要のないレベルがある場合は、読み込まないようにしてください。黄色のレベルは、できれば読み込まない、または保留するのが良いと言えます。大きなストリーミング レベルがある場合、分割するか最適化するようにしてください。

STAT MEMORY:

Stat Memory は、レベル内のアセットのメモリ使用量の基本的な概略を提供します。

  • オーディオ メモリ
  • Novodex 割り当て
  • アニメーション
  • 頂点ライティング
  • StaticMesh 頂点/インデックス
  • SkeletalMesh 頂点/インデックス
  • Decal 頂点/インデックス
  • VertexShader
  • PixelShader
  • テクスチャ プール サイズ
  • FaceFX

多くの場合、上記のアニメーション、SkeletalMesh、オーディオ、FaceFX などは、Unreal 参照システムのため、ゲーム データに相互関連しています。これに関しては、このドキュメントのもう少し先の方で詳しく述べていくことにします。

その他の便利な コンソール コマンド:

  • ListSpawnedActors
  • ListLoadedPackages
  • ListPrecacheMapPackages
  • OBJ BULK

最適化に関する詳細は レベル最適化ガイド ページを、統計値の解説に関しては Stat コマンド解説 ページを参照してください。

エンジン構成

テクスチャ プール サイズ:

レンダリング ターゲット以外のすべてのテクスチャは、テクスチャ プールを使用します。これは、ライトマップを含みます。(ライトマップはシステム メモリを変動させないので、頂点ライティングの代わりにこれを使用することは、メモリ節約の点において完全な勝利と言えるでしょう。)

また、ゲームに最適なテクスチャ プール サイズがあることを確認してください。 Stat Memory は、テクスチャ プールのどれぐらいが使用されているかを表示してくれます。これに基づいて、ゲームのための最適な数を決めることができるのです。

*Engine.ini にて
      [TextureStreaming]
         PoolSize=120

頻繁なガーベジ コレクション:

ゲームにより頻繁な ガーベジ コレクション が必要な場合は、この構成を使用してください。

*Engine.ini にて
      [Engine.Engine]
         TimeBetweenPurgingPendingKillObjects=10 (10 秒ごと)

アニメーション最適化

AnimSet がゲームによって参照されている場合、AnimSet のすべてのアニメーションを読み込みます。それらの一部分しか使用しない場合は、これは無駄になります。AnimSet 内のすべてのアニメーションが必要ない場合、通常サブ AnimSet に分割し、必要な場合のみに読み込むようにすることが推奨されます。

例えば、すべての武器のアニメーションが常に必要でない限り、異なる武器はそれぞれの AnimSet を持つことができます。

Matinee:

メモリの問題は Matinee に関しては、より深刻です。

ワークフロー (異なる人員が異なる Matinee シーケンスの作業をしている場合など) によっては、Matinee シーケンス、またはレベルごとに異なる AnimSet を維持することは、非常に厄介な場合があります。レベルごとに 1 つの AnimSet を持つことが理想なのですが、異なるデベロッパー グループ間 (LD/アニメーター/スクリプター) でこれを維持することは、非常に時間を食う可能性があります。

InterpData 内のフラグである bShouldBakeAndPrune (CL 259515) は、このような状況の手助けをしてくれます。これは、レベルの新しい AnimSet を作成し、そのレベルに使用されるアニメーションのみを複製して、参照に再リンクします。それでも AnimSet がクックされてしまう/読み込まれてしまう場合は、同じアニメーションを複製するので、このフラグをオフにする必要があります。ベイクして整理している AnimSet が、レベルにより読み込まれる必要がない場合は、これを使用する必要があります。*Editor.ini の Cooker.MatineeOptions セクション内の bBakeAndPruneDuringCook 値を FALSE に設定することで、フラグをグローバルで無効化することができます。

*Editor.ini にて
      [Cooker.MatineeOptions]
         bBakeAndPruneDuringCook=false

アニメーション圧縮:

アニメーションの圧縮は、アニメーション メモリの節約に大きな役割を果たします。より詳細な情報は、 アニメーション圧縮 ページを参照してください。

参照システム

ゲームのレベルが完成に近づくにつれて、必要なアセットのみが読み込まれていることを確認するのに、すべてを見返す価値が十分あるでしょう。Unreal Engine 3 では、参照されたコンテンツはすべてゲーム内で読み込まれ、直接的、間接的の両方でコンテンツを参照する方法が数多くあるので、気付かないうちに大きなアセットを読み込んでしまっていることがあります。例えば、骨格メッシュを参照しているポーンは、骨格メッシュに参照されている AnimSet を自動的に読み込みます。従って、すべてのアニメーション (Pawn -> Skeletalmesh -> Animsets -> アニメーション) を読み込むのです。ゲームは、それ以上のアクタがお互いを参照するので、この連鎖関係はメモリの観点から危険であると言えます。

読み込まれるべきでないものが読み込まれているか、またはストリーミング レベルに渡って分割されることができるものが読み込まれているか、そしてすべてが同時に読み込まれていないかどうかを決定するには、通常レベル デザイナーと一緒に作業をする必要があります。例としては、このレベルに存在しない敵のメッシュなどがあります。

MEMLEAKCHECK:

Memleakcheck は、多くの基本的なゲーム データを、テキスト ファイルとして 1 つの出力ファイルに [ProfileDirectory:Platform-specific]/MemLeak 内で表示します。

少なくともこれは、骨格メッシュ、AnimSet、サウンドなどのオブジェクト リストのスナップショットを表示してくれます。基本的なスナップショットを手に入れたら、それらをチェックし、不必要なアセットが読み込まれていないかを調べることができます。読み込まれている場合は、誰がそれを参照しているかをトレースし、参照を削除することが解決のカギとなります。

以下は参考事例です。

OBJ LIST CLASS=SKELETALMESH:

すべての読み込まれた SkeletalMeshes をサイズでソートしてリスト化します。 MEMLEAKCHECK はこの情報を含みますが、アルファベット順でソートされるので、最適化するコンテンツを探すのが困難です。

LISTSOUNDS:

すべての読み込まれた SoundCues をサイズでソートしてリスト化します。 MEMLEAKCHECK はこの情報を含みますが、これもアルファベット順でソートされるので、最適化するコンテンツを探すのが困難です。

OBJ LIST CLASS=STATICMESH: すべての読み込まれた StaticMeshes をサイズでソートしてリスト化します。

LISTANIMSETS:

すべての読み込まれた AnimSets をサイズでソートしてリスト化します。 MEMLEAKCHECK もこの情報を含みます。

OBJ REFS:

なぜアセットが読み込まれているかを調べるもっとも有効な方法は、 OBJ REFS コマンドを使用することです。これを使用するには、再帰関数のため深いスタックを必要とし、通常 UE3 のコンソール バージョンをクラッシュさせるので、ゲームを PC で起動する必要があります。

問題のアセットが読み込まれるポイントまでプレイし、以下を入力してください: OBJ REFS CLASS= NAME=

例は以下の通りです。

OBJ REFS CLASS=SKELETALMESH NAME=BIG_OGRE_2

すると、アセットを引き出している参照の連鎖が何であるかを以下のように表示します。

Log: Shortest reachability from root to SkeletalMesh COG_Bolo_Grenade.Frag_Grenade:
Log:    SkeletalMesh COG_Bolo_Grenade.Frag_Grenade [target] (root) (standalone)
Log:    SkeletalMeshComponent GearGame.Default__GearProj_FragGrenade:COGGrenadeMesh0 (root) (ObjectProperty Engine.SkeletalMeshComponent:SkeletalMesh)
Log:    Class GearGame.GearProj_FragGrenade (root) (standalone)

上記の例では、Frag_Grenade メッシュが、GearProj_FragGrenade Native クラスのデフォルト プロパティに参照されています。

1 つ以上の参照がある場合があることに注意してください。その場合、1 つずつ処理していかなければいけません。

以下は、アセットが間違って読み込まれている場合の理由のいくつかになります。

  • アセットが Native クラスに参照されている。
  • UnrealScript コードが、そのデフォルト プロパティを得るために (アセットを参照する) クラスを参照している。
                      Asset = class'MyGameContent.Pawn_BigOgre'.default.Mesh.PhysicsAsset;
  • タッチ Kismet イベント (SeqEvt_Touch) が、その ClassProximityTypes または IgnoredClassProximityTypes 配列内でクラスを参照している。

メモリ フラグメンテーション

MEM STAT: 概要のみ

MEM DETAILED:

これは、アロケータに便利な情報を表示します。 以下 のプラットフォーム セクションを参照してください。

メモリ フラグメンテーションのためにバッファを維持することは、良い方法です。

スタック メモリ

スタック メモリは、メイン スレッドによって継承されます。作成するスレッドをどのスタック サイズにするかは指定することができますが、スレッドを内部で作成するライブラリを使用している場合、メモリを考慮しないことがあります。

参考

以下のページでは、メモリに関してより詳細な情報を提供しています。

コンソール上のメモリ

便利なコマンド