Evolution/Kinematic update and forceが重いケースの対処方法について

お世話になっております

現在制作中のゲームにて、一部のシーンが重いとの事でプロファイルを行った所​

Evolution/Kinematic update and force が非常に重い状態となっており、

どうやらChaosのAccelerrationStructureの更新に甚大な時間が掛かってしまっている様でした

乱暴な方法では​

​p.Chaos.AccelerationStructureUseDynamicTree 0

​とする事でほぼコストが消え去った状態ではありますが、

根本的にはアセット側の対処が必要かと思っております

ただ、再現できるのがパッケージビルドのみであり、

エディタ環境ではEvolution/Kinematic update and force以下の処理がボトルネックとして検出されない挙動となっていました

今の所対処の見当として考えているのが

・とりあえずオブジェクトをNoCollisionにしまくってみる

​・何か動く床等でBPで動かしているメッシュがあれば、UseParentBounds等にチェックを入れてまとめてみる

等を考えている所ですが、こういった対処方法でも見当は外れてい無さそうでしょうか?

また、もしこの箇所がボトルネックになるケースで他に何か知見や考慮すべき対処方法等がありましたら、ご教授頂けますでしょうか

添付画像は実際の現象が起こっているパッケージビルドでのUnreal Insightsでのプロファイル結果となります

(なぜか、EditorのPIEでは再現はできない挙動となっています)

よろしくお願いいたします​

​[Image Removed]

お世話になっております。

ご質問ありがとうございます。

一般に、コリジョンを持つ動的なオブジェクトの数を減らせば ComputeIntermediateSpatialAccelerationの負荷を下げることは確かに可能ですが、現在の250msは非常に大きな値であり、アセット側の対策に取り組む前に、もう少し調査・検証を行っていければと存じます。差し支えない範囲で、以下の点についてお尋ねしてもよろしいでしょうか?

・World Partitionを利用されていますでしょうか?

・重たい「一部のシーン」でマップのストリーミングロードが実行されていますでしょうか?

(PIE実行ではエディタの特性上メモリに積みっぱなしになるものを、パッケージビルドではストリーミングしていて、それがAcceleration Structureの更新をトリガーしているのであれば、差異の説明となります)

・「重たいシーン」は、ご指摘のパフォーマンス項目が「常に」高負荷でしたでしょうか? あるいは「数秒で収まる」ものでしょうか?

・「重たいシーン」では、床など、何かオブジェクトが動いていますでしょうか?またそのオブジェクトは相対的に大きなものでしょうか?それともキャラクターと同じくらいのサイズのものでしょうか?

・「重たいシーン」では、毎フレーム、(ヒット判定やオーバーラップ判定を持つ)オブジェクトをスポーンする、クエリを飛ばすなど、何か物理的に重たい処理に心当たりがありますでしょうか?

・「重たいシーン」を除く、通常のシーンでは、Evolution/Kinematic update and forces の負荷は、それほどでもないでしょうか?

※負荷の計測にあたってはUnreal Insightが頼りになりますが、今回の「Evolution/Kinematic update and forces」や「ComputeIntermediateSpationalAcceleration」の大体の負荷の値をチェックする用途であれば、コンソールコマンド「stat physics」を使って画面に負荷値を表示する方法も、手っ取り早いやり方としてご利用いただけます。

なお、p.Chaos.AccelerationStructureUseDynamicTreeにつきましては、デフォルトの「1」 の設定を用いれば、動的なオブジェクトのコリジョンを効率よく処理することが可能ですが、 Dynamic Treeを用いたAcceleration Structure の構築がボトルネックになっている(データの準備コストが、データ利用時に得られる便益を上回る)のであれば、AccelerationStructureUseDynamicTree = 0 の設定も一つの解決策となります。実際、Epic Gamesの内製タイトルにおいても、こちらのCVarは0に設定しております。

また、PDBRigidsEvolution.cppにはAccelerationStructureUseDynamicTreeを初めとする、多くのCVarが定義されています。当方の立場では、問題のシーンの詳細が把握できておりませんので、どのCVarをチューニングすればパフォーマンス向上が見込めるかを申し上げることが難しいのですが、逆に気になるCVarがあれば調整をしていただき、効果のあったものを教えていただければ、そこから逆に原因を推測できるかもしれません。たとえば、AccelerationStructureUseDynamicTreeは1をキープしつつ、 p.Chaos.AccelerationStructureCacheOverlappingLeaves を0にう設定すると、どのような効果が得られるか(あるいは効果がまったくないか)ご確認いただくことは可能でしょうか。

※このCVarは「オーバーラップクエリを高速化するキャッシュを構築するかどうか」を決定するパラメータであり、そのコンセプトは「キャッシュ構築に払うコストをオーバーラップ処理の高速化で取り返す」というものになります。そのため、このトレードオフが成立しづらいプロジェクトでは、0に設定することをお勧めしています。

質問ばかりで大変申し訳ございません。

以上、よろしくお願いいたします。

>・「重たいシーン」は、ご指摘のパフォーマンス項目が「常に」高負荷でしたでしょうか? あるいは「数秒で収まる」ものでしょうか?

常に高負荷状態が持続しますが、少し波形は特長的でして、1,2フレーム間隔で200msec程の高負荷状態がギザギザと出る形となります

[Image Removed]

>「重たいシーン」では、床など、何かオブジェクトが動いていますでしょうか?またそのオブジェクトは相対的に大きなものでしょうか?それともキャラクターと同じくらいのサイズのものでしょうか?

動く床が2,3枚あり、キャラクタがその上に乗る事で移動できる様な形のオブジェクトがあります(エレベータの様な物となります)

>「重たいシーン」では、毎フレーム、(ヒット判定やオーバーラップ判定を持つ)オブジェクトをスポーンする、クエリを飛ばすなど、何か物理的に重たい処理に心当たりがありますでしょうか?

ゲームの要素として複数の敵キャラとプレイヤキャラクタが存在し(それぞれ5~6体ずつ、合計10体ほど)、それぞれが攻撃時にコリジョン等が発生し判定を取ったりしています。が、基本的にはずっと攻撃判定等が出続けている形ではない状態です。また、非同期で周囲のマテリアル情報をレイを3~5本程度飛ばしてチェックする様なロジックも入っておりますが、これも「重いシーン」以外でも常時行っている動作ではある為、特定のマップ以外では特に重い状況ではないという状況です

>「重たいシーン」を除く、通常のシーンでは、Evolution/Kinematic update and forces の負荷は、それほどでもないでしょうか?

はい、他のマップでは特に問題になったケースがなく、特定のマップのみに発生する形となっています

> p.Chaos.AccelerationStructureCacheOverlappingLeaves を0にう設定すると、どのような効果が得られるか(あるいは効果がまったくないか)ご確認いただくことは可能でしょうか

こちら、0に設定してみた所、エディタと同じ程度の想定していた負荷状況に戻りました!

細かい別のcVarでの検証等は追って検証を行ってみる予定で、取り急ぎ現状の情報となります

よろしくお願いいたします

> ・World Partitionを利用されていますでしょうか?

はい、レベルの管理についてはWorldPartitionを利用しています

> ・重たい「一部のシーン」でマップのストリーミングロードが実行されていますでしょうか?

はい、DataLayerを併用してWorldPartition上で部分的なストリーミングロードを行っております

>p.Chaos.AccelerationStructureCacheOverlappingLeavesを1に戻しつつ、p.Chaos.AccelerationStructureTimeSlicingMaxQueueSizeBeforeForceの値を大幅に引き上げる

p.Chaos.AccelerationStructureTimeSlicingMaxQueueSizeBeforeForceを100000等の数に上げてみたものの、特に効果はなさそうな感じでした

追加情報として以下を試してみましたが、こちらは効果がありませんでした

p.Chaos.AccelerationStructureSplitStaticDynamic 1 → 0 に変更 (Array index out of bounds: -1 into an array of size 6511でクラッシュ)

p.Chaos.AccelerationStructureUseDirtyTreeInstedOfGrid 1 → 0に変更(効果無し)

p.Chaos.AccelerationStructureTimeSlicingMaxBytesCopy → 1~10000000で値を変更(効果無し

以上となります

よろしくお願いいたします

お世話になっております。

ご確認ありがとうございます。

>>・「重たいシーン」は、ご指摘のパフォーマンス項目が「常に」高負荷でしたでしょうか? あるいは「数秒で収まる」ものでしょうか?

> 常に高負荷状態が持続しますが、少し波形は特長的でして、1,2フレーム間隔で200msec程の高負荷状態がギザギザと出る形となります

重要な情報をありがとうございます。今回のパフォーマンスヒットの震源地である「ComputeIntermediateSpationalAcceleration」関数は、AABB Treeの更新が必要な要素が積まれたキューをタイムスライス処理する関数なのですが、そのキューの要素数がp.Chaos.AccelerationStructureTimeSlicingMaxQueueSizeBeforeForceで指定された値(初期値:1000)を超えると、フルビルドと呼ばれる一括処理に切り替わるという特徴があります。

おそらく問題となっているシーンではキューに投入された要素の数が1000付近にあり、タイムスライスで済むフレームとフルビルドがかかるフレームが数フレーム毎に入れ替わっており、ご提示いただいたような特徴的なギザギザの負荷を示す結果に繋がったものと考えられます。

このような状況では、p.Chaos.AccelerationStructureCacheOverlappingLeavesを1に戻しつつ、p.Chaos.​AccelerationStructureTimeSlicingMaxQueueSizeBeforeForceの値を大幅に引き上げることで、負荷の平均化を図ることも可能ですが、ランタイムのコリジョン処理周りで負荷が悪目立ちしていないのであれば、前者のフラグを0に設定するアプローチのほうが妥当と思われます。

なお、問題となったシーンでDynamicTreeに多くの更新が必要になった理由を背景情報として把握できればと思うのですが、下記の質問について回答をいただくのは、難しいでしょうか・・・?

> ・World Partitionを利用されていますでしょうか?

> ・重たい「一部のシーン」でマップのストリーミングロードが実行されていますでしょうか?

以上、よろしくお願いいたします。​

お世話になっております。

設定のご確認および詳細な情報のご共有、誠にありがとうございます。

ご報告いただいた状況から推察するに、先日ご共有いただいた「負荷グラフのギザギザ」​は、物理の非同期処理の影響によるものと考えられます。

(AccelerationStructureCacheOverlappingLeavesが1に設定されている場合に呼び出される、今回の主な負荷要因であるCacheOverlappingLeaves()は物理Tickごとに実行されます。そのため、Insights上のゲームフレームごとの負荷表示としてはバラつきやすく、特にゲームのフレームレートが低下している状況では、ギザギザとしたパターンが顕著に現れるようです)

社内の関係者によると、​CacheOverlappingLeaves()の処理負荷はシーン内の動的オブジェクトの数に比例するとのことでした。また、ある大きなコリジョンが、より小さなコリジョンを包含するような配置が行われている場合、CacheOverlappingLeaves()の仕事が複雑になり、パフォーマンスに影響を与える可能性があります。

対処法としましては、

・キャッシュ作成のコストが、キャッシュから得られる効率性と不釣り合いであるとして、p.chaos.AccelerationStructureCacheOverlappingLeaves = 0の設定を採用する

・不要なMoveable可動性オブジェクトを削減するなど、シーン構成を見直す

といったものが考えられます。​

また、もし「動く床」の上に多数の装飾品などのオブジェクトがアタッチされて一緒に動いている場合、シーン内の「動的オブジェクト」の数が実際には非常に多くなっている可能性もあります。この場合、初回の投稿で示唆されていた「コリジョンが不要なオブジェクトをNo Collisionに設定する」という対策は、効果的であると考えられます。一方、Use Attach Parent Boundsが今回のようなシナリオで(負荷削減に)期待通りの効果を発揮するかどうかについては、こちらでも引き続き詳細を確認させていただきます。

以上、よろしくお願いいたします。​

>社内の関係者によると、CacheOverlappingLeaves()の処理負荷はシーン内の動的オブジェクトの数に比例するとのことでした。また、ある大きなコリジョンが、より小さなコリジョンを包含するような配置が行われている場合、>CacheOverlappingLeaves()の仕事が複雑になり、パフォーマンスに影響を与える可能性があります。

知見のご共有ありがとうございます

オブジェクト数やコリジョンの内包状態等を意識して、該当箇所が不必要にあるケースでは対処していきたいと思います

>対処法としましては、

>・キャッシュ作成のコストが、キャッシュから得られる効率性と不釣り合いであるとして、p.chaos.AccelerationStructureCacheOverlappingLeaves = 0の設定を採用する

>・不要なMoveable可動性オブジェクトを削減するなど、シーン構成を見直す

p.chaos.AccelerationStructureCacheOverlappingLeaves = 0の設定につきまして、Epic様の内製ゲームでの運用事例もあり、現在相談している弊社シチュエーションにおいてもこちらの方がパフォーマンスが出ている状態でありますので、

一旦こちらの値を設定してしばらく様子を見る形となりそうです

>・不要なMoveable可動性オブジェクトを削減するなど、シーン構成を見直す

こちらについても、不必要に設定している箇所がありましたら対処を入れて行こうかと思います

>「コリジョンが不要なオブジェクトをNo Collisionに設定する」

これについては、ご相談と並行して作業を進めておりましたが、対応を入れた後で一定量のパフォーマンス改善がされており、改めて効果を確認できました

ありがとうございます

>Use Attach Parent Boundsが今回のようなシナリオで(負荷削減に)期待通りの効果を発揮するかどうかについて

これについて、もし追加で知見がありましたらご共有頂ければ幸いです

以上、よろしくお願いいたします

お世話になっております。

ご回答ありがとうございます。

> p.chaos.AccelerationStructureCacheOverlappingLeaves = 0の設定につきまして、Epic様の内製ゲームでの運用事例もあり、現在相談している弊社シチュエーションにおいてもこちらの方がパフォーマンスが出ている状態でありますので、

> 一旦こちらの値を設定してしばらく様子を見る形となりそうです

誤解の生じる書き方をしてしまい申し訳ありません。正確に申し上げますと、社内で 0 に設定しているのは、​p.Chaos.AccelerationStructureUseDynamicTreeのほうとなります(こちらを切ると、連動してCacheOverlappingLeaves()も呼び出されなくなります)。

ただし、他のライセンシー様に ​p.chaos.AccelerationStructureCacheOverlappingLeaves = 0 の設定をお勧めし、御社と同様に採用された前例がいくつかございます。こちらのほうが、AccelerationStructureUseDynamicTreeより狭い範囲でChaosの機能を切る(有益な機能をより多く残す)形になりますので、この設定でうまく動作しているのであれば、Epic内製タイトルでの使用/不使用にかかわらず、問題のない選択かと存じます。

>>Use Attach Parent Boundsが今回のようなシナリオで(負荷削減に)期待通りの効果を発揮するかどうかについて

>これについて、もし追加で知見がありましたらご共有頂ければ幸いです

こちらについてですが、当方で再現プロジェクトを作成したうえでテストをしようと考えております。

「動く床」と連動して移動するもの(例:床の上に乗っているオブジェクト、装飾品)を、主にどのような手法で「動く床」にアタッチしているか、ご共有いただくことは可能でしょうか?

・「動く床」のBP内のコンポーネントとして付随させているのか?

・あるいは、アーティストが「動く床」​の上にオブジェクトを積んだうえで、アウトライナーを用いて親子関係づけを行っているのか?

・あるいは、プログラム/BPでアタッチ命令をかけてアタッチしているのか?

など、可能な範囲で情報共有いただけますと幸いです。

以上、よろしくお願いいたします。​