Android での Unreal ゲームのメモリ最適化

Android ゲーム開発では、メモリ使用量が非常に重要になります。 Unreal Engine を使用してゲームを開発する場合は、メモリリークや過剰なメモリ割り当てを防ぐために、メモリ状態を継続的に 分析して追跡し、これらのメモリ制限を満たす必要があります。

シェーダーの順列の削減によるメモリの節約

Lighting または Mobile Shader Permutation Reduction で未使用のオプションを削除すると、シェーダーの順列の数を最小限に抑えることができます。これにより、アプリのパッケージ サイズが大幅に削減され、パイプライン状態オブジェクト(PSO)の数が減り、未使用のレンダー ターゲットやダミー テクスチャの作成を防ぐことができるため、ランタイム メモリが削減され、パフォーマンスが向上します。

詳細については、モバイル デバイスのパフォーマンス ガイドラインモバイル向けのその他のライティングとシェーダーの順列 の削減の設定セクションをご覧ください。

アセットの読み込みの最適化

ハード参照を使用してアセットを読み込むと、ゲームで使用されていない接続されたコンテンツも同時にメモリに読み込まれるため、不要なメモリ消費が発生します。特に、ブループリントで継承が誤用されている場合、親から不要な CDO(クラスのデフォルト オブジェクト)が割り当てられ、大量のメモリが無駄になります。このような場合は、次の手法を適用してメモリ使用量を最適化します。

  • C++ コードの定義とデータの分離: コア機能を定義します C++ コードで UCLASS(Abstract) を使用して、実際のデータから分離します。
  • 継承を最小限に抑える: リーフノードのブループリントでのみ、データの設定とアセットの読み込みを行います。
  • 間接参照を使用する: TSoftObjectPtrを使用して間接プロパティ参照を適用し、ハード参照を回避します。また、ランタイムで必要な場合にのみアセットを 非同期で読み込みます。
  • 注意事項: 非同期アセットの読み込みを使用する場合は、読み込み完了コールバック中に有効性チェック (IsValid)を実行して、 ダングリング ポインタやクラッシュを防ぎます。

詳細については、アセットの参照UE5 を使用したモバイルゲームのビルドをご覧ください。

スマート ポインタとガベージ コレクション(GC)のチューニング

C++ 開発でスマート ポインタ(TSharedPtrTWeakPtr など)を使用すると、ダングリング ポインタやメモリリークを防ぐことができます。また、TWeakPtr を使用すると、オブジェクトへの強参照が作成されないため、ガベージ コレクタがオブジェクトを収集するときに参照カウントが増加しません。これにより、不要なオブジェクトの存続を防ぎ、GC がトラバースする必要がある参照グラフの複雑さを軽減し、GC のオーバーヘッドを大幅に削減できます。レベルの切り替え時や、ユーザー インタラクションがない場合などの非アクティブな期間に ForceGarbageCollection をトリガーすることで、モバイル環境でガベージ コレクションを効果的に管理できます。また、ゲームが認識可能な状態またはバックグラウンド状態(onStop コールバックなど)の場合にトリガーして、アクティブなゲームプレイ エクスペリエンスに影響を与えることなくガベージ コレクションを実行できます。

詳細については、ガベージ コレクションスマート ポインタをご覧ください。

再配置テーブルの圧縮を使用した最適化

再配置テーブルの圧縮は、ELF バイナリの 再配置テーブルのサイズを圧縮してAndroid バイナリのサイズを削減する最適化手法です。Android minSDKVersion が 23 以上の場合は、APS 再配置テーブルの圧縮がサポートされます。minSDKVersion が 28 以上の場合は、RELR 再配置テーブルの圧縮もサポートされ、.so ファイルのサイズが大幅に削減されます。これにより、ダウンロード サイズが小さくなるだけでなく、ランタイム時の .so mmap のメモリ使用量も削減され、物理メモリの全体的な負荷が軽減されます。

AndroidToolChain.cs

if (MinSDKVersion >= 28)
{
  Result += " -Wl,--pack-dyn-relocs=android+relr,--use-android-relr-tags";
}
else if (MinSDKVersion >= 23)
{
  Result += " -Wl,--pack-dyn-relocs=android";
}

この最適化を適用するには、[プロジェクト設定] の [再配置テーブルの圧縮を有効にする] または bEnableAdvancedBinaryCompression 変数が有効になっていることを確認します。

RELR 圧縮がバイナリに適切に適用されているかどうかを確認するには、Android NDK の llvm-readelf ツールを使用します。

llvm-readelf -d libUnreal.so | grep RELR

実際には、Unreal Engine の ThirdPerson テンプレートをビルドして dumpsys meminfo で測定すると、.so mmap の PSS と RSS の使用量が削減されていることが確認できます。

カテゴリ PSS 合計 プライベート ダーティ プライベート クリーン スワップ ダーティ RSS 合計
.so mmap 圧縮前 170,210 24,360 141,564 0 242,204
.so mmap 圧縮後 138,031 24,748 108,976 0 210,140

テクスチャの圧縮

適切なテクスチャ圧縮を使用して、グラフィック リソース全体のメモリ使用量を削減します。モバイル標準の Adaptive Scalable Texture Compression(ASTC)を使用すると、同じ サイズで ETC2 よりも優れた画質を実現できます。また、同じ画質目標でメモリ消費量を抑えながら、より高い圧縮率を実現できます。

テクスチャ ストリーミングの最適化

モバイル環境では、デフォルトの Unreal Engine テクスチャ ストリーミング プールサイズを直接使用すると、デバイスの仕様に対して過度に大きなメモリプールが事前に割り当てられる可能性があります。そのため、モバイル デバイス プロファイルを定義し、デバイスのメモリ制限に応じて適切なテクスチャ ストリーミング プールサイズを選択することをおすすめします。また、エディタの [統計情報] 機能を使用してテクスチャの統計情報を確認し、ランタイム時に極端な解像度を占有するメモリ消費量の多いテクスチャを特定します。これを [必要なテクスチャ解像度] 機能と組み合わせて、要求の厳しいテクスチャの制限を適用したり、最大解像度を下げたりすることで、割り当てられたテクスチャ メモリバッファ内でリソース使用量を制御できます。