circle-loader
by
103/ 0

今日は記事の第 3 部を紹介します: エンジンモジュールでのCPU 時間のチューニング、レンダリングモジュール、UI モジュール、物理モジュール、アニメーションモジュール、パーティクルシステム、ロードモジュール、ロジックコード、Lua など合計 9 つのセクションが含まれ、よく見られるゲームCPU時間のチューニングについて解説します。

(全文は約22,260文字で、読むのは約30分かかります)


1.概要

1.1 モジュール分割

UWA では、作業内容が明確で、CPU時間のかかる関数を、レンダリング、UI、物理、アニメーション、パーティクル、ロード、ロジック、およびその他のモジュールに分割します。しかし、それは各モジュールが互いに独立して完全に無関係であるわけではありません。例を挙げれば、レンダリングモジュールのパフォーマンスは、複雑な UI とパーティクルの影響を受けることが避けられず、ロードモジュールの多くの操作は実際にロジックで呼び出されて完了したのです。

 

モジュールを分割するのは、問題を特定し、ポイントを見つけるためですが、その同時に、モジュール間に関連を付けるのも必要です。関連を付けるのは、問題をより効率的に解決するのに役立ちます。

 

1.2 時間コストボトルネック

CPU 側の性能ボトルネックでフレームレートが低くなってフリーズが明らかな場合、一体どのモジュールのどの問題が性能ボトルネックを引き起こしたのかを明確にするのは鍵となります。エンジン内のメインモジュールを整理しましたところで、各モジュールで発生する問題は様々で同一視してはいけません。しかも、CPUへのプレッシャーもそれぞれ異なっています。そのため、潜在的性能ボトルネックと見なされるCPU時間を正確に理解する必要があります。

 

モバイル プロジェクトでは、CPU パフォーマンスの最適化の目標は、ローエンドおよびミッドレンジデバイスでほとんどの場合 30 フレームのスムーズなゲーム プロセスを実行できるようにすることです。この目標を達成するには、割り算を行って平均 CPU 時間を 33 ms未満に制御する必要があるとわかります。もちろんですが、33 ms以内のプロジェクトが 適切に制御していることを意味しているわけではありません。なぜならば、ゲームの実行中、性能へのプレッシャー作用点は異なっているからです。例えば、一連の UI インターフェイスのわけでプレッシャーがほとんどないが、逆にゲームで最も重要な戦闘シーンのフレームレートが非常に低く、または数百msさらに数秒のフリーズがあるけれども、最終的な平均値は33 ms未満であることがあるかもしれません。

 

このため、UWA は、テストで、33ms 以上をかかるフレーム数が全フレーム数の 10% 未満である場合、プロジェクトの全体的な CPU パフォーマンスが通常の範囲内に制御されていると見なすことができると考えています。割合が高いほど、現在のプロジェクトの CPU パフォーマンスのボトルネックが深刻です。

 

上記の議論は主に、CPU パフォーマンスのマクロ最適化の目標を中心に展開しています. メモリと同様に、特定のモジュールの特定のデータを組み合わせて、プロジェクトの実際の問題をトラブルシューティングおよび解決する必要があります。


2.レンダリングモジュール

レンダリングモジュールの最適化に関するより包括的なコンテンツについては、「Unity パフォーマンス最適化シリーズ – レンダリング モジュール」を参照してください。

 

2.1 マルチスレッドレンダリング

一般に、シングルスレッド レンダリング プロセスでは、ゲームの各フレーム中に、メイン スレッド (CPU1) が最初に Update を実行し、ここでゲーム AI、衝突検出、アニメーションの更新などの多数の論理更新を実行します。次にRenderを実行し、ここでレンダリング関連の命令呼び出しを行います。レンダリング時、メイン スレッドはグラフィックス API を呼び出して、シェーダー、テクスチャ、マトリックス、 アルファブレンディング(α合成)の設定などのレンダリング状態を更新してから、DrawCall を実行する必要があります。これらすべてのグラフィックス API 呼び出しは、ドライバー レイヤーと交互します。すべてのレンダリング状態について、ドライバー レイヤーによって維持されています。これらの API を呼び出すと、ドライバー層のレンダリング状態が変化し、フリーズが発生する可能性があります。ドライバー層の状態は上位層の呼び出しに対して透過的であるため、フリーズが発生するかどうか、およびフリーズ期間は API の呼び出し元 (CPU1) にはわかりません。このとき、他の CPU がアイドル待ち状態になる可能性があり、無駄になります。したがって、レンダリング部分を抽出して他のCPUに配置して、別のレンダリングスレッドを形成することができます。これは、メインスレッドのジャミングを減らすために論理スレッドと同時に実行されます。

 

大まかな実装プロセスというと、メイン スレッドで呼び出されるグラフィックス API がコマンドにカプセル化され、レンダリング キューに送信されます。これにより、メイン スレッドでグラフィックス API を呼び出すオーバーヘッドが節約され、フレーム レートが向上します。レンダリング スレッドは、レンダリング キューからレンダリング命令を受け、呼び出したグラフィックス API とドライバー レイヤーとの交互を実行します。この部分の時間コストには、メイン スレッドからレンダリング スレッドまでに移動されます。

Unity は、プロジェクト設定でマルチスレッド レンダリングをサポートし、デフォルトで有効にします。通常は、有効にしておくことをお勧めします。 UWA の大量のテスト データでは、一部のプロジェクトでマルチスレッド レンダリングがオフになっていることが判明されています。マルチスレッド レンダリングがオンになっている場合、GPU 作業完了までのCPU待機時間は Gfx.WaitForPresent 関数でカウントされ、マルチスレッド レンダリングがオフになっている場合、この部分の時間はGraphics.PresentAndSync でカウントされます。したがって、Gfx.WaitForPresent 関数の時間コストがプロジェクトでカウントされるかどうかが、マルチスレッド レンダリングが有効かどうかの判断基準になります。特に、プロジェクトの開発段階とテスト段階では、レンダリング モジュールのパフォーマンスのボトルネックをより直感的に反映するために、一時的にマルチスレッド レンダリングをオフにしパッケージ化してテストすることを検討できます。

 

マルチスレッド レンダリングが通常有効になっているプロジェクトの場合、Gfx.WaitForPresent の時間コスト傾向もかなりの参考になります。テストでローカル GPU の負荷が高いほど、GPU が作業を完了するまで CPU が待機する時間が長くなり、Gfx.WaitForPresent に必要な時間が長くなります。したがって、Gfx.WaitForPresent が数十ミリ秒または数百ミリ秒続く場合、対応するシーンの GPU 負荷が比較的高いことを意味します。

さらに、多数のプロジェクトと UWA のテスト経験によると、過剰な GPU 負荷は、レンダリング モジュール (Camera.Render および RenderPipelineManager.DoRenderLoop_Internal) の CPU 側の主要な関数の時間消費も増加させます。最後に、特に GPU 部分の最適化について説明します。

 

2.2 同じ画面のレンダリング面数

レンダリング効率に影響を与える 2 つの最も基本的なパラメーターは、間違いなく Triangle と DrawCall です。

 

通常、三角形の面の数は GPU のレンダリング時間に比例し、ほとんどのプロジェクトでは、不透明な三角形の数が半透明の三角形の数よりもはるかに多いため、特別な注意が必要です。 UWA では一般的に、同一画面上のレンダリング面の数をローエンド モデルで 250,000 面以内に抑えることを推奨しており、ハイエンド モデルでも 600,000 面を超えることは推奨されていません。このツールを使用して、同じ画面でのローカルレンダリング面数が多すぎることがわかった場合は、フレーム デバッガーを使用して、キー フレームのレンダリング オブジェクトを確認できます。

 

一般的な最適化方法は、制作上、メッシュ リソースの面数を厳密に制御することです。特に一部のキャラクターや地形モデルでは、数万面以上のメッシュを特に注意してください。また、LOD ツールを使用してシーン内の面数を減らすことができます。例えば、ローエンドデバイスでロー ポリゴンを使用して、シーン内の比較的重要でない小さなオブジェクトの表示を減らすことでレンダリングのコストを削減します。

UWA ツールが注目・計算する面数は、現在のフレームのシーン モデルの面数ではなく、現在のフレームでレンダリングされた面数です。その値はモデル の面数だけでなく、レンダリング回数にも関係します。これは、同じ画面のレンダリング面数によって引き起こされるレンダリング プレッシャーをより直感的に反映できます。例: シーン内のメッシュ モデル 面数が 10,000 で、使用するShaderに 2 つのレンダリング パスがあるか、2 台のカメラが同時にレンダリングするか、または SSAO や反射などの後処理効果の 1 つが使用されているかの場合、ここに表示される三角形の値は 20,000 になります。したがって、ローエンドデバイスではレンダリング面数が 2 倍にする操作に十分に注意してください。ハイエンドでもちゃんとトレードオフしてください。

 

2.3 バッチ (DrawCall)

Unity では、DrawCall と Batch を区別する必要があります。バッチには複数の DrawCall がある場合、常にバッチの数をより重視する傾向があります。Batchはレンダリング データを GPU に送信するユニットであり、最適化および制御する必要がある実際のオブジェクトでもあるからです。

通常、バッチを削減するには、動的バッチ処理、静的バッチ処理、SRP バッチャー、GPU インスタンシングの 4 つの方法があります. バッチ最適化に関する議論はより複雑であり、別の記事を書いても過言ではないため、この記事ではこれ以上議論しません。ですが、 UWA DAY 2020 では、DrawCall と Batch の関係や、これら 4 種類のBatchingの使用方法の詳細な説明について、「Unity モバイル ゲーム プロジェクトの最適化事例分析 (その 1)」を参照してください

以下は、静的バッチ処理、SRP バッチャー、および GPU インスタンシングのバッチ処理条件、長所と短所を簡単にまとめたものです。

 

(1) 静的バッチ処理

条件: 異なるメッシュでも同じシェーダーが使用されます。

利点: 頂点情報のバインド、ジオメトリ情報の転送が節約されていました。隣接するマテリアルが同じ場合、マテリアルの転送が節約されていました。

短所: オフラインでマージする場合、マージされたメッシュに重複したリソースがあると、マージされたパッケージが大きくなりやすい; 実行時にマージする場合、結合メッシュを生成するプロセスで短時間の CPU ピークが発生する; マージしたMeshには重複したリソースがあるなら、マージ後のメモリ使用が増加します。

 

(2) SRPバッチャー

条件: 異なるメッシュでも同じシェーダーが使用され、かつ同じバリアントが使用されます。

利点: Uniform Buffer の書き込み操作を省く; Shader に従ってバッチを分割し、事前に Uniform Buffer を生成し、バッチ内に CPU Write がない.

短所: コンスタント バッファー (CBuffer) メモリ固定コスト; MaterialPropertyBlock はサポートされていません。

 

(3) GPU のインスタンス化

条件: 同じメッシュ、同じシェーダーが使用されています。

利点: 同種の多数のモンスターをレンダリングするニーズに適しており、バッチ処理中のアニメーション モジュールの消費時間を削減できます。

短所: マイナスの最適化が行われる可能性がありますが、代わりに DrawCall が増加します。インスタンス化が中断されることがあり、API レンダリングをグループで使用できます。

 

2.4 Shader.CreateGPUProgram

この API は、多くの場合、レンダー モジュールのメイン関数のスタックに表示され、レンダー モジュールでほとんどの関数スパイクを引き起こします。これは、シェーダーが初めてレンダリングされるときに生成される時間コストであり、その時間コストは、シェーダーのレンダリングの複雑さに関連しています。ゲームプレイ中に呼び出され、時間コストのピーク値が発生する場合は注意が必要です。

この点で、ShaderVariantCollection を介して Shader で使用されるバリアントを収集し、AssetBundle をパッケージ化できます。 ShaderVariantCollection リソースをメモリにロードした後、初期のゲーム シーンで ShaderVariantCollection.WarmUp を呼び出して Shader.CreateGPUProgram をトリガーし、SVC をキャッシュして、ゲームの実行中にこの API 呼び出しがトリガーされるのを回避し、ローカルでの高い CPU 消費時間を回避します。

 

ただし、上記の操作を行ったプロジェクトでも、実行時に API の消費時間に時折スパイクが検出されることがよくあります。これは、「ネットをすり抜ける魚」が存在することを示しています。開発者は、プロファイラーのタイムライン モードを組み合わせ、Shader.CreateGPUProgram への呼び出しをトリガーするフレームを選択して、どのシェーダーが API をトリガーするかを確認できます。「シェーダー バリアント コレクション、パッケージ化、コンパイル、および最適化のアイデア」を参照できます。

 

2.5 カリング

ほとんどの場合、カリング自体は時間がかかり、目立ちません。その重要性は、レンダリング関連の問題を反映することです。

 

(1) カメラの数が多い

レンダリング モジュールのメイン関数のスタックが、カリング時間の比較的高い割合 (一般的なプロジェクトで約 10% ~ 20%) を占める場合。

 

(2) シーン内に小さなオブジェクトがたくさんある

カリング時間は、シーン内のGameObjectの小さな物体の数と密接に関連しています。この場合、開発チームはシーンの制作方法を最適化し、シーン内に小さなオブジェクトが多すぎないかどうかに注意を払うことをお勧めします。動的読み込み、ブロック表示、または Culling Group や Culling Distance などの方法を使用して、Culling 時間を最適化することを検討できます。

 

(3) オクルージョンカリング

プロジェクトがマルチスレッド レンダリングを使用し、オクルージョン カリングがオンになっている場合、通常、サブスレッドへの負荷が高くなり、全体的なカリングが高くなりすぎます。

 

オクルージョン カリングはシーン内のオブジェクトに応じてオクルージョン関係を計算する必要があるため、オクルージョン カリングはレンダリング消費を削減しますが、それ自体のパフォーマンス コストも注目に値し、すべてのシーンに適用できるとは限りません。この場合、比較のために開発者はオクルージョン カリングの一部を選択的に無効にして、レンダリング データの全体的な消費をテストしてから、この機能を有効にするかどうかを決定することをお勧めします。

 

(4) バウンディングボックスの更新

Culling のスタックに時々現れる FinalizeUpdateRendererBoundingVolumes は、バウンディング ボックスの更新時間です。スキン メッシュとパーティクル システムのバウンディング ボックスの更新でよく見られます。 API が頻繁に表示される場合は、スクリーンショットを使用して、現時点で多数のスキン メッシュの更新またはより複雑なパーティクル システムの更新があるかどうかを確認する必要があります。

 

(5) PostProcessingLayer.OnPreCull/WaterReflection.OnWillRenderObjectPostProcessLayer.OnPreCull

メソッド  は、プロジェクトで使用される PostProcessing Stack に関連しています。 PostProcessManager.cs に静的変数 GlobalNeedUpdateSettings を追加し、シーンを切替えるときに PostProcessManager.GlobalNeedUpdateSettings を true に設定することで UpdateSettings を実行できます。このようにして、フレームごとに UpdateSettings 操作を実行することを避けることができるため、時間コストを減らすことができます。

WaterReflection.OnWillRenderObject は、プロジェクトで使用されている水面反射効果に関連する時間コストです。この時間コストが大きい場合、不要なパーティクルや小さなオブジェクトなどの反射レンダリングを削除するなど、実装方法に最適化の余地があるかどうかに注意してください。

3.UIモジュール

Unity エンジンでは、主流の UI フレームワークには UGUI、NGUI、それにますます使用されるようになっているFairyGUI があります。この記事では、主に最も使用されている UGUI から説明します。 UGUI 関連の最適化に関するより包括的なコンテンツについては、「Unity パフォーマンスの最適化 – UI モジュール」を参照してください。

 

3.1 UGUI EventSystem.Update

EventSystem.Update 関数はUGUI のイベントシステムの時間です。時間コストが高い場合、主に次の 2 つの要因が関係しています。

 

(1) トリガー呼び出しに時間がかかる

UGUI イベント システムの主な関数として、この関数は主にタッチが離されたときにトリガーされます。CPU 時間コストが高い場合、通常、より時間のかかる他の関数を呼び出すことによって引き起こされます。そのため、Profiler.BeginSample/EndSample 管理または GOT Online サービス + UWA API 管理を追加して、トリガーされたロジックをさらに検出し、どのサブ関数またはコード セグメントが時間のかかる原因であるかを見つける必要があります。

 

(2) ポーリングに時間がかかる

すべての UGUI コンポーネントは、デフォルトで有効になっている Raycast ターゲットのオプションを使用して作成されます。これは、実際にイベント応答を受け入れる準備ができています。実際、Image や Text などのほとんどの UI コンポーネントはイベント応答に参加しませんが、マウス/指がスワイプまたはホバーされたときにポーリングに参加するため、UI コンポーネントがスワイプされたかどうかは光線検出をシミュレートして判断されます。またはホバリングすると、不要な時間がかかります。特にプロジェクト内に多くの UI コンポーネントがある場合、イベント応答に関与しないコンポーネントの Raycast Target 設定をオフにすると、EventSystem.Update() の時間を効果的に短縮できます。

 

3.2 UGUI Canvas.SendWillRenderCanvases

Canvas.SendWillRenderCanvases 関数にかかる時間は、UI 要素自体の更新にかかる時間であり、Canvas.BuildBatch のメッシュ再構築にかかる時間とは異なります (以下を参照)。

 

多くの場合、UI 要素が複雑すぎて更新頻度が高すぎると、長時間の高消費が続くことがあります。自己更新 UI 要素には、画像の置き換え、テキストや色の変更などがあります。この関数では、UI 要素の移動、回転、またはスケーリングによってオーバーヘッドが発生することはありません。この関数にかかる時間は、更新される UI 要素の数と UI 要素の複雑さによって異なります。そのため、この関数のコストを最適化するには、通常、次の点から始めることができます。

 

(1) 頻繁に更新されるUI要素の頻度を減らす

例えば、ミニマップ上のモンスターマーク、キャラクターやモンスターのヘルスバーなど、変化が一定の閾値を超えた場合にのみUI表示を更新するロジックを制御したり、スキルCD効果、ダメージなどフレームごとの更新を制御する浮遊文字など。

 

(2) 複雑な UI を変更しないようにする

たとえば、一部の文字列が非常に大きく、リッチ テキスト、アウトライン、または影付きのテキスト効果が使用され、画像の種類がタイル画像であるとします。頂点の数が多いため、これらの UI 要素が更新されると、更新に時間がかかります。 Outline や Shadowmap を使用する必要があるが、浮動ダメージ数など頻繁に変化するエフェクトがある場合は、頂点の数が N 倍にならないように固定アーティスティック ワードにすることを検討できます。

 

(3) Font.CacheFontForText に注意する

この関数は、時間のかかるスパイクを引き起こす傾向があります。この API は主に動的フォントの Font Texture を生成するためのオーバーヘッドであり、実行時に非常に時間がかかります。一度に多くの新しい文字が書き込まれる可能性が非常に高く、その結果、Font Texture テクスチャが拡張されます.フォントの種類を減らしたり、フォント サイズを小さくしたり、一般的な単語を事前に表示してダイナミック フォントの FontTexture を拡張したりすることで、この項目の時間を最適化できます。

 

3.3 UGUI Canvas.BuildBatch

Canvas.BuildBatch は、UI 要素のマージされたメッシュを変更する必要があるときに呼び出されます。通常、前述の Canvas.SendWillRenderCanvases() 呼び出しにより、Canvas.BuildBatch が呼び出されます。さらに、Canvas 内の UI 要素の移動によっても、Canvas.BuildBatch が呼び出されます。

 

Canvas.BuildBatch は、メイン スレッドで UI メッシュのマージを開始します。特定のマージ プロセスは、子スレッドで処理されます。子スレッドへの負荷が大きすぎる場合、またはマージされた UI メッシュが複雑すぎる場合、メイン スレッドで待機します。

待ち時間は EmitWorldScreenspaceCameraGeometry でカウントされます。

 

これら 2 つの関数は時間がかかるのは, 再構築された Canvas が非常に複雑であることを示しています。このとき、Canvas を細分化する必要があります。通常、静的要素は Canvas に配置され、更新された UI 要素は Canvas に配置されます。静的キャンバスがキャッシュのためにメッシュを更新しないようにします。これにより、メッシュ更新の複雑さが軽減され、メッシュの再構築にかかる時間が短縮されます。

 

3.4 UGUI CanvasRenderer.SyncTransform

一部のプロジェクトの一部のフレームで、CanvasRenderer.SyncTransform が頻繁に呼び出されることがよくあります。下図に示すように、CanvasRenderer.SyncTransform は最大 1017 回呼び出されます。 Canvas.SyncTransform が非常に頻繁にトリガーされると、その親ノード UGUI.Rendering.UpdateBathes に非常に長い時間がかかります。

 

Unity 2018 以降のバージョンでは、Canvas の下の UI 要素で SetActive (false を true に変更) を呼び出すと、Canvas の下の他の UI 要素が SyncTransform をトリガーし、UI 更新の全体的なコストが増加します。Unity 2017バージョンは、UI 要素自体が SyncTransform をトリガーするだけです。

 

そのため、UI 要素 (Image や Text など) が多い Canvas の場合、SetActive になることが多い UI 要素が存在しないかどうかに注意する必要があります。 これに対し、SetActive (false または true)の代わりにSetScale(0や1)を使ってください。または、Canvas を適切に分割して、SetActive (true) にする必要がある要素と他の要素が同じ Canvas の下にないようにして、SyncTransform が頻繁に呼び出されないようにすることもできます。

 

3.5 UGUI UI DrawCall

通常、戦闘シーンの他のモジュールは時間がかかることがあります。このとき、UI モジュールはパフォーマンスのオーバーヘッドを慎重に制御する必要があります。一般的には、戦闘シーンでの UI DrawCall は 40 ~ 50 程度に制御するのが最適です。

 

UI要素を減らさないという前提で、DrawCall問題を制御するのは、実際にはUI要素をできるだけバッチ処理する方法の問題です。一般的なバッチ処理では同じマテリアルが必要ですが、UI では、同じマテリアルと同じアトラスで作成された UI 要素をバッチ処理できないことがよくあります。これは、実は UGUI DrawCall の計算原理に関係しています。原理の詳細な紹介については、UWA アカデミーのコース「詳細な UGUI DrawCall 計算とリビルド操作の最適化」を参照してください。

 

UGUI の作成プロセスでは、次の点に注意することをお勧めします。

(1) 同じ Canvas の下にある UI 要素は、まとめてのみバッチ処理できます。 Order in Layer が同じでも Canvas が異なると承認されないため、合理的な UI の企画と制作が非常に重要です。

(2) 異なる UI 要素のマテリアル アトラスが一貫するように、アトラスの統合と作成を試みます。アトラスのボタン、アイコンなどは、画像の比較的小さな UI 要素を使用する必要があり、それらを統合してアトラスに作成できます。それらが同時に密集して表示されると、DrawCall が効果的に減少します。

(3) 同じ Canvas と同じマテリアルとアトラスを前提として、散在するレベルを避ける。簡単にまとめると、バッチ処理条件を満たす UI 要素の「レベルの深さ」は同じでなければなりません。

(4) 該当する UI の Pos Z をできるだけ 0 にする Z 値が 0 でない UI 要素は Hierarchy 内の隣接する要素とのみバッチ処理を試みることができるため、バッチ処理を中断しやすくなります。

(5) アルファが 0 のイメージの場合、その CanvasRender コンポーネントで [透過メッシュのカリング] オプションをオンにする必要があります。そうしないと、DrawCall が引き続き生成され、バッチが簡単に中断されます。


4.物理モジュール

物理モジュールの最適化に関するより包括的なコンテンツについては、「Unity パフォーマンスの最適化 – 物理モジュール」を参照してください。

 

4.1 Auto Simulation

Unity 2017.4 バージョン以降、物理シミュレーションの自動シミュレーション設定オプションが有効になり、デフォルトで有効になります。つまり、物理シミュレーションは、プロジェクト中に常にデフォルトで実行されます。しかし、場合によっては、この部分の時間が無駄になります。

 

物理シミュレーションの時間が無駄になっているかどうかを判断する基準の 1 つは、Contactsの数、つまりゲーム実行中の衝突ペアの数です。一般に、衝突ペアの数が多いほど、物理システムの CPU 負荷が高くなります。しかし、多くのモバイル プロジェクトでは、ゲーム全体を通して連絡先の数が常に 0 であることを検出しました。

 

この場合、開発者はテストのために物理の自動シミュレーションをオフにすることができます。自動シミュレーションをオフにしてもゲーム ロジックに影響がなく、ゲーム中に良好な対話や戦闘などを行うことができる場合は、この点で時間を節約できることを意味します。また、プロジェクトで光線検出を使用する必要がある場合は、自動シミュレーションをオフにしてから自動同期変換をオンにして、光線検出が正常に機能するようにする必要があることにも注意してください。

 

4.2 物理的な更新の数

Unity の物理シミュレーション プロセスの主な時間のかかる関数は FixedUpdate にあります。つまり、フレームごとのこの関数の呼び出し数が多いほど、物理的な更新の回数が多くなり、フレームごとの時間がかかります。相応に高い。

 

物理的な更新の数、または FixedUpdate のフレームごとの呼び出しの数は、Unity プロジェクト設定の時間設定の最小更新間隔 (Fixed Timestep) と最大許容時間 (Maximum Allowed Timestep) に関連しています。ここで、物理システム自体の特性を知る必要があります、つまり、ゲームが最後のフレームで動かなくなった場合、Unity は現在のフレームの非常に早い段階で FixedUpdate.PhysicsFixedUpdate を N 回連続して呼び出します。 Timestep は、物理的な更新の数を制限することです。1 フレーム内の物理呼び出しの最大数を決定し、値が小さいほど、1 フレーム内の物理呼び出しの最大数が少なくなります。これら 2 つの値をそれぞれ 20ms と 100ms に設定すると、フレームが 30ms かかる場合、物理的な更新は 1 回だけ実行され、200ms かかる場合は 5 回だけ実行されます。

したがって、これら 2 つのパラメーターの設定を調整すること、特に更新回数の上限を制御することが有効な方法です (デフォルトは 17 回であり、5 回未満に制御するのが最適です)。物理モジュールの消費時間はあまり高くなりません. 一方では、他のモジュールの CPU 時間消費が最初に最適化されます. プロジェクトの実行中に時間のかかるフレームが少ない場合、FixedUpdate は常に上限に達するとは限りません.フレームあたりの更新数。これは、FixedUpdate の他の関数でも同じです。このため、通常、FixedUpdate であまり多くのゲーム ロジックを記述することはお勧めしません。

 

4.3 Contacts

前述のように、物理シミュレーションを使用する場合、一般的に衝突ペアの数が多いほど、物理システムがより多くの CPU 時間を必要とします。したがって、衝突ペアの数を厳密に制御することは、物理モジュールの時間を短縮するために非常に重要です。

 

まず第一に、多くのプロジェクトには不要な Rigidbody コンポーネントが存在する可能性があり、開発者が知らない場所で不要な衝突が発生し、時間の浪費につながります。さらに、プロジェクト設定 、レイヤー間の不要な衝突検出をキャンセルし、Contactsの数を可能な限り減らします。


 

5.アニメーションモジュール

アニメーション モジュールの最適化に関するより包括的なコンテンツについては、「Unity パフォーマンスの最適化 – アニメーション モジュール」を参照してください。

 

5.1 Mecanim アニメーション システム

Mechanic アニメーション システムは、Unity 4.0 以降に Unity によって導入された新しいバージョンのアニメーション システムです (Animator を使用してアニメーションを制御します)。Legacy のアニメーション制御システムと比較して、Mecanim アニメーション システムには主に次の利点があります。

(1) アバターの作成や筋肉の調整など、ヒューマノイド キャラクター用の特別なワークフローが用意されています。

(2) あるキャラクター モデルから別のキャラクター モデルにアニメーションを簡単に適用できるアニメーション リターゲット機能。

(3) ビジュアル アニメーター エディターが提供され、アニメーション クリップをすばやくプレビューして作成できます。

(4) 状態マシンと状態間の遷移を作成する方が便利です。

(5) 操作が簡単なハイブリッドツリー機能。

 

パフォーマンスの面では、スケルタル アニメーションと多くの曲線を含むアニメーションの場合、アニメーターはマルチスレッド コンピューティングをサポートし、アニメーターは最適化されたゲームオブジェクトを有効にすることで最適化できるため、アニメーターを使用した場合のパフォーマンスはアニメーションよりも優れています. 詳細については、を参照してください。 UWA アカデミー コース「Unity モバイル ゲームにおけるアニメーション システムのパフォーマンスの最適化」。逆に、移動や回転などの単純なアニメーションの場合、アニメーション コントロールを使用する方がアニメーターよりも効率的です。

 

5.2 ベイクメッシュ

MOBAやSLGの兵士など、顔の数が少なくアニメーション時間が短いオブジェクトの場合は、CPUの置き換えに時間がかかるSkinnedMeshRenderer.BakeMeshのスキームが考えられます。メモリー。原則は、スキニング アニメーションの特定の時点でのアクションをスキニングなしでメッシュにベイクすることです。これにより、アニメーションをカスタム サンプリング間隔で一連のメッシュ シーケンス フレームに変換できます。次に、アニメーションを再生するときに、最も近いサンプリング ポイント (つまり、メッシュ) を選択して割り当てるだけで済みます。これにより、ボーンの更新とスキニングの計算にかかる時間を節約できます (アニメーションはほとんどなく、割り当てのみです)。全体の操作は、スキニング計算を節約できるため、パッチの数が少ないキャラクターに適しています。その機能は次のとおりです。計算時間のためにメモリを交換します。シーンに多数の同じアニメーション モデルが表示されると、その効果は非常に明白になります。この方法の欠点は、モデル頂点の数、合計アニメーション時間、およびサンプリング間隔によってメモリ使用量が大幅に制限されることです。したがって、この方法は、頂点の数が少なく、合計アニメーション時間が短いモデルにのみ適しています。同時に、Bake は時間がかかり、シーンをロードするときに完了する必要があります。

 

5.3 Active Animator数

アクティブ状態の Animator の数は、アニメーション モジュールの処理時間に大きく影響し、定量化できる重要な基準であり、この数を比較的妥当な値に制御することは、アニメーション モジュールを最適化するための重要な手段です。開発者は、画面に基づいて、対応する数値が妥当かどうかを確認する必要があります。

(1)アニメーターカリングモード

Active Animator を制御する 1 つの方法は、アニメーション コンポーネントごとに適切な Animator.CullingMode 設定を調整することです。この設定には、AlwaysAnimate、CullUpdateTransforms、および CullComplete の 3 つのオプションがあります。

 

デフォルトの AlwaysAnimate では、現在のオブジェクトがビューポート ボディにあるか、LOD カリングがビューポート ボディにドロップされているかに関係なく、アニメーター内のすべてが更新されます。その中でも、UI アニメーションには AlwaysAnimate を選択する必要があります。そうしないと、異常なパフォーマンスが発生します。

 

CullUpdateTransforms に設定されている場合、オブジェクトが視野にない場合、または LOD によって Culled された後、ロジックは更新を続けます。つまり、ステート マシンが更新され、アニメーション リソースの接続条件も更新されます。ただし、Retarget、IK、C++ からの return Transform などの表示レイヤーの更新は行われません。したがって、パフォーマンスに影響を与えずにいくつかのアニメーション コンポーネントを CullUpdateTransforms に設定しようとすると、オブジェクトが表示されていないときにアニメーション モジュールの表示レイヤーの時間を節約できます。

 

最後に, CullComplete はまったく更新されません. シーン内の比較的重要でないアニメーション効果に適しています. ローエンドのマシンでは, 表示する必要があるが静的と見なすことができるオブジェクトは階層的に選択されます.

 

(2) DOTween プラグイン

多くの場合、UI アニメーションも多くの Active Animator に貢献しています。色の変更、ズーム、移動、その他の効果など、一部の単純な UI アニメーションについては、代わりに DOTween を使用することを UWA は推奨しています。テスト後、パフォーマンスはネイティブ UI アニメーションよりもはるかに優れています。

 

5.4 Apply Root Motion を有効にするアニメーターの数

Animators.Update のスタックでは、Animator.ApplyBuiltinRootMotion のアカウントが多すぎることが時々見られます. この項目は通常、プロジェクトで Apply Root Motion が有効になっているモデル アニメーションに関連しています.アニメーションがディスプレイスメントを必要としない場合、このオプションをオンにする必要はありません。

5.5 Animator.Initialize

Animator.Initialize API は、Animator コンポーネントを含む GameObject がアクティブでインスタンス化されるとトリガーされますが、これには時間がかかります。したがって、特に戦闘シーンでは、アニメーターを含むゲームオブジェクトに対して非アクティブ/アクティブ ゲームオブジェクト操作を頻繁に実行することはお勧めしません。頻繁にインスタンス化されるキャラクターや UI については、バッファー プールを使用して対処することができます. キャラクターを非表示にする必要がある場合は、キャラクターの GameObject を直接無効にするのではなく、Animator コンポーネントを無効にし、GameObject をオフにします.画面; UI を非表示にする必要がある場合 UI オブジェクトが直接 Deactive ではなく、SetScale=0 で画面の外に移動した場合、Animator.Initialize はトリガーされません。

 

5.6 Meshskinning.Update と Animators.WriteJob

時間のかかるアニメーション モジュールに対するメッシュ リソースの影響は非常に大きくなります。

 

一方で、Meshkinning.Update にかかる時間は長くなります。主な要因は、スキン メッシュのボーンとパッチの数が多いため、メッシュ リソースを削減し、LOD グレーディングを行うことができることです。

 

一方、デフォルト設定では、多くのプロジェクトのキャラクターのスケルトン ノードのトランスフォームが常にシーンに存在することがよくあります。そのため、ネイティブ レイヤーがトランスフォームを計算した後、それらは C# レイヤーに戻されます。その結果、一定の時間がかかります。

 

多数のキャラクターを含むシーンでは、スケルトン ノードが返されると一定のオーバーヘッドが発生します。これは、アニメーション モジュールのメイン関数の 1 つである PreLateUpdate.DirectorUpdateAnimationEnd の Animators.WriteJob サブ関数に反映されます。

 

このため、開発者は、FBX リソースの [リグ] タブにある [ゲーム オブジェクトの最適化] 設定をチェックして、スケルトン ノードを「非表示」にし、この部分にかかる時間を短縮することを検討できます。

 

5.7 GPU スキニング/コンピュート スキニング

特に Unity エンジン固有の GPU スキニング設定 (Unity の新バージョンではコンピュート スキニング) については、理論上はメッシュやアニメーションの更新方法をある程度変更して、骨格アニメーションの処理を最適化する予定ですが、複数のテスト結果によると、iOS プラットフォームでも Android プラットフォームでも、複数の Unity バージョンで提供される GPU スキニングは、パフォーマンスの向上に明白な効果はなく、マイナスの最適化さえあります。 Unity のイテレーションで徐々に最適化されており、関連する操作はレンダリング スレッドに入れられていますが、その実用性についてはさらに調査が必要です。

 

同種のモンスターが多数必要な場合は、UWA オープン ソース ライブラリの「GPU スキニング アクセラレーテッド スケルタル アニメーション」と GPU インスタンシングの独自の実装をレンダリングに使用することを検討できます。 Animator.Update だけでなく、バ​​ッチ処理も実現します。


6.パーティクルシステム

パーティクル システムの最適化に関するより包括的なコンテンツについては、「パーティクル システムの最適化 – スキル効果を最適化する方法」を参照してください。

 

6.1 再生中のパーティクル システムの数

UWA は、パーティクル システムの数と再生状態のパーティクル システムの数をカウントします。前者は、再生中のものとバッファ プール内のものを含む、メモリ内のすべての ParticleSystems の総数を指し、後者は、オンスクリーンとオフスクリーンの両方を含む、再生中の ParticleSystem コンポーネントの数を指します。フレーム内に存在するピークの数が 50 を超えないことをお勧めします (1GB モデル)。

 

これらの 2 つの値については、一方で、パーティクル システムのピーク数が多すぎるかどうかに注意を払い、ピーク フレームを選択して、どのパーティクル システムがキャッシュされているか、それらが妥当かどうか、過剰なキャッシュがあるかどうかを確認します。一方、私たちは再生数に注意を払います。ピーク値が高すぎるかどうか、ピーク フレームを選択して、どのパーティクル システムが再生されているか、それらがすべて妥当かどうか、およびいくつかの生産最適化を行うことができるかどうかを確認できます (詳細については、以下の GPU セクションの説明を参照してください)。

 

6.2 Prewarm

ParticleSystem.Prewarm にかかる時間も問題になることがあります。 Prewarm オプションがオンになっているパーティクル システムがシーンでインスタンス化されるか、非アクティブからアクティブに変わると、完全なシミュレーションがすぐに実行されます。

 

ただし、通常、Prewarm の操作にはある程度の時間がかかります. テスト後、Prewarm と SetActive を有効にする多数のパーティクル システムでは、時間のかかるピークが発生します。不要な場合はオフにすることをお勧めします。


 

7.ローディング モジュール

ローディング モジュールの最適化に関するより包括的なコンテンツについては、Unityパフォーマンス最適化シリーズ —— ローディングとアセット管理を参照してください。

 

7.1 シェーダーのロード

(1) Shader.Parse

Shader.Parse は Shader の読み込みと解析の操作を指します. この操作が頻繁に行われる場合, 通常は Shader の繰り返し読み込みが原因です. ここでの繰り返しは 2 層の意味として理解できます.

 

最初のレイヤーはシェーダーの冗長性によって引き起こされます。通常、アセットバンドルがパッケージ化されると、シェーダーは依存関係のパッケージ化なしで複数の異なるアセットバンドルに受動的に入力されるため、これらのアセットバンドルのリソースが読み込まれると、これらのシェーダーは受動的に読み込まれます。 および複数の「繰り返される」 Shader.Parse が実行されるため、同じ Shader がメモリ内に複数のコピーを持つことになり、冗長になります。

 

この冗長性を取り除く方法も非常に簡単です。これは、これらの冗長なシェーダーの依存関係を共通の AssetBundle パッケージにパッケージ化することです。これは、このシェーダーを使用する一部のパッケージを受動的に入力するのではなく、積極的にパッケージ化します。この Shader がアクティブにパッケージ化されている場合、この Shader を使用する他の A​​ssetBundle は、この Shader によって生成されたパブリック AssetBundle のみを参照するため、メモリ内には Shader が 1 つだけ存在し、他の Shader は使用時に使用されます。 Shader.Parse を複数回実行する必要があります。

 

2 番目のレイヤーは、同じシェーダーがキャッシュなしで複数回ロードおよびアンロードされることを意味します。 AssetBundle がアクティブにパッケージ化され、パブリックの AssetBundle が生成されると仮定すると、メモリにはこのシェーダーしかありませんが、シェーダーが読み込まれる (つまり、Shader.Parse) ため、キャッシュされず、使用後すぐにアンロードされます。 .次にこの Shader を使用するときは、メモリに Shader がないため、再ロードする必要があります。そのため、同じ Shader が複数回読み込まれて解析され、結果として複数の Shader.Parse が生成されます。一般的に言えば、バリアント最適化後に開発者が作成したシェーダーは、多くのメモリを消費せず、ゲームの開始時に均一にロードおよびキャッシュできます。

 

特に、Unity のビルトイン シェーダーの場合、バリアントの数が少ない限り、このタイプのシェーダーの重複した繰り返しの解析を避けるために、常にプロジェクト設定に含めることができます。

 

(2) Shader.CreateGPUProgram

この API は、ロード モジュールのメイン関数や UI モジュール、ロジック コードのスタックにも表示されます。関連する説明は上記で説明されており、最適化方法も同じであるため、ここでは繰り返しません。

 

7.2 Resources.UnloadUnusedAssets

この API は、シーンの切り替え時に Unity によって自動的に呼び出されます. 通常、1 回の呼び出しには時間がかかります. 通常、手動で呼び出すことはお勧めしません.

 

ただし、シーンの切り替えや Additive を使用したシーンの読み込みを行わない一部のプロジェクトでは、この API が呼び出されないため、プロジェクトの全体的なリソースとメモリの数が増加傾向にあります。この場合、5 ~ 10 分ごとに手動で呼び出すことを検討できます。

 

Resources.UnloadUnusedAssets の基本的な動作メカニズムは、リソースごとに、階層ツリー内のすべてのゲームオブジェクト ノードとヒープ メモリ内のオブジェクトを走査して、リソースがゲームオブジェクトまたはオブジェクト (コンポーネント) によって使用されているかどうかを検出することです。使用されていない場合、エンジンはそれを未使用のリソースとして認識し、アンインストール操作を実行します。簡単に言えば、Resources.UnloadUnusedAssets の 1 回の消費量は、((GameObjects の数 + Mono オブジェクトの数) * Assets の数) の積が増加するにつれて、おおよそ増加します。

 

したがって、このプロセスは非常に時間がかかり、シーン内のゲームオブジェクト/アセットの数が多く、ヒープ メモリ内のオブジェクトの数が多いほど、オーバーヘッドが大きくなります。これに関して、私たちの推奨事項は次のとおりです。

 

(1) Resources.UnloadAsset/AssetBundle.Unload(True)

The R&D team can try to remove a resource that has been dedevet to dedevet to be no longer used to Resources.UnloadAsset/AssetBundle.Unload(True) when the game is running. これら 2 つの API は非常に効率的であり、 Resources.UnloadUnusedAssets の時間的プレッシャーにより、シーンの切り替え時にかかる API の時間が短縮されます。

 

(2) シーンで使用されるマテリアル リソースとパーティクル システムの量を厳密に制御します。

これらの 2 つのリソースは特に言及されています。ほとんどのプロジェクトでは、それらのメモリ使用量は一般的に大きなものではありませんが、リソースの数は他のタイプのリソースよりもはるかに多く、簡単に数千のオーダーに達する可能性があるためです。 UnloadUnusedAssets は、時間のかかる作業に大きく貢献しています。

 

(3) 常駐ヒープメモリを減らします。

ヒープ内のオブジェクトの数も、上で説明した Resources.UnloadUnusedAssets にかかる時間に大きく影響します。

 

7.3 アセットバンドルの読み込み

AssetBundle を使用してリソースをロードすることは、モバイル プロジェクトでは一般的な方法です。

 

このうち、AssetBundle は可能な限り LZ4 圧縮形式でパッケージ化し、LoadFromFile の方法でロードする必要があります。テスト後、この組み合わせで大きな AssetBundle パッケージ (10 個の 1024*1024 テクスチャを含む) でさえ、読み込みに 10 分の数ミリ秒しかかかりません。 LoadFromMemory などの他の読み込み方法を使用すると、読み込み時間が数十ミリ秒に増加しますが、WebRequest 読み込みを使用すると、AssetBundle パッケージの常駐メモリが大幅に増加します。

 

これは、LoadFromFile が、ハードディスクや SD カードなどのローカル ストレージから非圧縮または LZ4 圧縮形式のアセットバンドルをロードするための効率的な API であるためです。

 

デスクトップ スタンドアロン プラットフォーム、コンソール、およびモバイル プラットフォームでは、API は AssetBundle のヘッダーのみをロードし、残りのデータはディスクに残します。 AssetBundle のオブジェクトは、ロード メソッド (例: AssetBundle.Load) が呼び出されたとき、またはその InstanceID が間接的に参照されたときなど、オンデマンドでロードされます。この場合、メモリをあまり消費しません。

 

ただし、エディター環境では、ディスクからバイトを読み取って AssetBundle.LoadFromMemoryAsync を使用する場合と同様に、API はアセットバンドル全体をメモリにロードします。この API は、プロジェクトがエディターでプロファイリングされている場合、AssetBundle の読み込み中にメモリ スパイクを引き起こす可能性があります。ただし、これはデバイスのパフォーマンスに影響を与えるべきではなく、これらのスパイクは最適化を行う前にデバイスで再テストする必要があります。

 

この API は、非圧縮または LZ4 圧縮形式専用であることに注意してください。LZMA 圧縮を使用すると、生成されたデータ パッケージ全体が圧縮されるため、圧縮解除する前にアセットバンドルのヘッダー情報を取得することはできません。

 

LoadFromMemory の読み込み効率は他のインターフェイスよりも大幅に時間がかかるため、大規模な使用はお勧めしません。また、ヒープ メモリが大きくなります。 AssetBundle ファイルを暗号化する必要がある場合は、テクスチャやメッシュなどのリソース ファイルを暗号化せずに、重要な構成ファイルやコードなどのみを暗号化することを検討できます。テクスチャ、メッシュなどのレンダリング関連のリソースを低レベルの方法で取得およびエクスポートできるツールが市場に出回っているため、リソースのこの部分を暗号化する必要はあまりありません。

 

UWA GOT オンライン リソース モードのリソース管理ページでは、ロードに時間がかかるアセットバンドルをトラブルシューティングして、ロード方法、圧縮形式、パッケージ サイズなどの問題をトラブルシューティングおよび最適化したり、ロードされたアセットバンドルをキャッシュすることを検討したりできます。繰り返しロード。

 

7.4 リソースのロード

リソースの読み込みにかかる時間については、読み込み戦略が適切であれば、通常はゲームの開始時とシーンの切り替え時に発生し、重大なパフォーマンスのボトルネックにはならないことがよくあります。ただし、いくつかの状況に注意を払う必要があることは否定できないため、トラブルシューティングの基礎として、時間のかかるリソースの読み込みの並べ替えを使用できます。

 

一度にロードするのに時間がかかりすぎるリソース (数百ミリ秒または数秒など) については、そのようなリソースが複雑すぎるかどうかを調べ、運用上の考慮事項からそれらを単純化する必要があります。

 

頻繁にロードされて時間がかかるリソースの場合は、最初のロード後にキャッシュして、ロードの繰り返しによるオーバーヘッドを回避する必要があります。

 

Unity の非同期読み込みでは、各フレームの読み込みに使用できる最大時間が制限されることがありますが、メイン スレッドはアイドル状態です。特にシーンカット時は非同期読み込みが集中し、時には数十秒、時には数十秒かかることもあるが、ほとんどの時間はアイドリングで浪費されている。これは、各フレームの最も時間のかかる非同期読み込みを制御する API Application.backgroundLoadingPriority がデフォルトで BelowNormal に設定され、各フレームが最大 4 ミリ秒でしか読み込まれないためです。現時点では、通常、この値を High に設定することをお勧めします。つまり、フレームあたり最大 50 ミリ秒です。

 

UWA GOT オンライン リソース モードのリソース管理ページでは、読み込みに時間がかかるリソースを確認して、読み込み方法を確認して最適化したり、リソースが複雑すぎたり、繰り返し読み込まれたリソースをキャッシュすることを検討したりできます。

 

7.5 インスタンス化と破棄

インスタンス化は、主に、単一のリソースのインスタンス化に時間がかかりすぎる、または特定のリソースが繰り返し頻繁にインスタンス化されるという現象にも存在します。時間のかかるものに応じて整理した後、問題が疑われるリソースについては、前者は単純化を検討するか、フレームごとの操作を検討します.たとえば、より複雑な UI Prefab の場合、目立つ重要なインターフェイスをインスタンス化するように変更し、次に、ページめくりコンテンツ、装飾アイコンなどがインスタンス化され、後者はキャッシュ プールを確立し、明示的および暗黙的な操作を使用して、頻繁なインスタンス化を置き換えます。

 

UWA GOT オンライン リソース モードのリソース管理ページでは、インスタンス化に時間がかかるリソースをトラブルシューティングして、過度に複雑なリソースの問題をトラブルシューティングして最適化したり、繰り返しインスタンス化されたリソースのキャッシュを検討したりできます。

 

7.6 アクティブ化と非表示

起動や非表示自体にかかる時間は高くありませんが、1 フレームでの操作が多すぎる場合は注意が必要です。多くのプロジェクトでは、特定のリソースの明示的および非表示の操作が多すぎて、SetActive(True) の数が SetActive(False) の数よりもはるかに多くなっています。 、または反対の現象、つまり、不要な SetActive 呼び出しがたくさんあります。 SetActive API は C# と Native 間のクロスレイヤー呼び出しを生成するため、数が増えると、依然としてかなりの時間がかかります。このような状況を踏まえ、ロジックが最適化できるかどうかを確認するだけでなく、ロジック内に状態キャッシュを構築し、API を呼び出す前に現在のリソースの活性化状態を判断することも検討できます。これは、ロジックのオーバーヘッドを使用して API のオーバーヘッドを置き換えることと同等であり、比較的時間がかかりません。

 

UWA GOT オンライン リソース モードのリソース管理ページでは、頻繁にアクティブ化および非表示になるリソースを確認して、関連するロジックと呼び出しを確認して最適化することができます。


 

8.ロジックコード

ロジックコードのCPU時間のかかる最適化は、プロジェクトの実際のニーズに基づいてプログラマー自身をテストするプロセスであり、定量的および定性的に議論することは困難です.しかし、UWA SDK は開発者がロジック コードを管理するのに便利な API&UWA GOT Online を提供し、複雑な関数を逆アセンブルし、レポートでスタックをチェックして最適化効果をより迅速に検証します。

 

JobSystem を使用して、メインスレッドの一部のロジック コードをサブスレッドに配置して処理するチームが増えていることがわかりました.並列に動作できるロジックについては、サブスレッドに配置して処理することを強くお勧めします.論理演算を処理するためのメイン スレッド CPU への負荷を効果的に軽減します。

 

9.ルア

GOT Online Luaモードが提供するLuaによるCPU負荷を解析するツールは可視化度が高く、スタックが明確で分かりやすく、実用的で特徴的な逆順呼び出し解析機能も備えています。以下は、Lua レポートと組み合わせたものです。デモでは、このツールを使用して時間のかかる Lua を分析する方法を簡単に紹介します。

 

繰り返しますが、Lua レポートに表示される関数名の形式は、関数名@ファイル名:行番号です。

 

レポートで提供されるLuaファイル名/行番号/関数名から、CPU時間のボトルネック機能とCPU時間のピークの具体的な原因を突き止めることができます。 Lua 関数の命名形式は X@Y:Z で、X は関数名です。取得できない場合、X はデフォルトの不明になります。Y は関数が定義されているファイルの場所です。Z は関数が定義されている行です。関数が定義されています。 Lua スクリプトがバイトコードとして実行される場合、この値は常に 0 になることに注意してください。そのため、テスト時にはできるだけ Lua ソース コードを使用して実行することをお勧めします。

 

(1) ポジティブシーケンスコール解析 – 集計表 (曲線+リスト)

グラフ:

 

曲線は、Lua コードの全体的な時間消費と、平均時間消費に従って並べ替えられた最初の 5 つの関数の時間消費を選択し、時間消費曲線グラフを形成します. 各データ ポイントは、関数の時間消費 (縦軸) を表しますこれは、時間のかかるボトルネック関数を特定するのに役立ちます。

 

リスト:

 

デフォルトでは、リストは平均消費時間に従って Lua 関数をソートし、関数名、合計 CPU 時間、シーン CPU 時間、および平均消費時間などのデータを大まかに表示します。関数をクリックすると、対応する単一関数の分析ページに入ることができます。

 

(2) ポジティブ シーケンス コール解析 – 単一関数ページ (スクリーンショット + グラフ + スタック情報)

スクリーンショット:

 

プロジェクトの実行中、スクリーンショットはユーザーが選択したフレームにほぼ対応しているため、問題を特定するのに役立ちます。

 

グラフ:

 

曲線グラフには、CPU 時間の消費曲線と呼び出しの数が含まれます。また、下のズーム曲線を使用して、ローカル時間の消費を観察することもできます。

 

グラフから、次のことを観察できます: 関数が連続して非常に時間がかかるかどうか、関数が短時間に大量の時間がかかり、結果としてフリーズするかどうか、一部の関数が 1 回しか時間がかからないかどうか高くはありませんが、多くの関数を呼び出すため、関数の合計時間は高くなります。

 

関数 XXXX スタック情報 (リスト):

 

このうち、リストデータの時間範囲は右上隅で選択でき、全体スタック情報が指定されている場合は時間範囲がテスト時間全体、シーンスタック情報が指定されている場合は時間範囲がオープニングです。指定されたシーンの時間; フレーム スタック情報が指定されている場合、時間範囲はグラフで現在選択されている指定されたフレームです。

 

リスト内の各インジケータの意味は次のとおりです。ルート ノード関数の合計時間消費を 100% とした全体の割合、ルート ノードの合計時間消費に対する現在のノード関数の合計時間消費の割合関数; 総所要時間は 100% であり、これは、ルート ノード関数の総所要時間に対する現在のノード関数自体の所要時間の割合です; 総所要時間、実行にかかる時間時間範囲内の関数; 自己時間がかかる、時間範囲内で子ノード関数が削除される ( 関数によって呼び出される関数) 時間がかかり、残りの時間がかかる; 呼び出し回数、関数の回数時間範囲内で呼び出された; 1 回の時間のかかる、時間のかかる/呼び出しの合計時間で、毎回関数を実行するのにかかる平均時間を示します; 重要な呼び出しフレーム数、関数自体が 3 ミリ秒以上のフレームを要します。

 

(3)リバースオーダーコール分析 – 集計表(曲線+リスト)

曲線グラフ: 順方向シーケンス コール分析とは異なり、時間のかかる順方向の最初の 5 つの関数が選択され、各データ ポイントは、現在のフレーム (横座標) における関数の時間のかかる (縦座標) を表します。

 

リスト: 上と同じ。

 

(4) 逆順呼び出し解析 – 単機能ページ (スクリーンショット+グラフ+スタック情報)

 

関数 XXXX スタック情報 (リスト):

各指標の意味(正のシーケンスとは異なります)は、独自の割合になり、選択された機能の自己消費時間の合計が 100% になり、この下で選択された機能の自己消費時間が 100% になります。呼び出しパスは、選択されたノードに相対的です. 関数の時間のかかる関数の合計の割合; 時間範囲内の時間のかかる関数自体, この呼び出しパスの下で選択された関数自体の合計の時間のかかる関数; 数呼び出しの数、この呼び出しパスの呼び出しの数、これを表す 1 回の消費時間 各呼び出しパスの下で選択された関数に費やされた平均時間。

 

上記のインターフェースを介して時間のかかる関数自体を特定した後、一般的な最適化方法には、関数自体の時間を短縮するために関数の関数本体を最適化する、呼び出しの多い呼び出しパスを特定し、呼び出し回数を減らす、などがあります。呼び出します。

 

(5) 注意事項

GC 時間は Lua の CPU 時間には含まれず、Lua の関数時間は関数の出入り時のチェックに相当し、統計時間は時間がかかります。したがって、Lua スクリプトが C# 関数を呼び出すと、C# 関数のこの部分がカウントされるため、C# での散在呼び出しの状況に注意し、50 回以内に抑えるようにする必要があります。

この記事の内容はここで紹介されています。より多くのコンテンツについては、UWAアカデミーにアクセスして読むことができます.このコースでは、現在のゲーム プロジェクトでよく発生するパフォーマンスの問題について、メモリ、CPU、GPU の 3 つの側面から説明します。

 


UWA公式サイト:https://jp.uwa4d.com

UWA公式ブログ:https://blog.jp.uwa4d.com

UWA公式Q&Aコミュニティ(中国語注意):https://answer.uwa4d.com