PIEにおけるNiagara System初回スポーン時のヒッチについて

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

PIEでNiagara SystemをスポーンするとUNiagaraSystem::RequestCompileが呼ばれ、ヒッチとなります。

ヒッチは初回スポーン時のみですが、エディタを起動しなおすと再び発生します。

UE5.6, UE5.7で確認しています。

クック後にヒッチはありません。

Async System Compilation が選択されているときにUNiagaraSystem::RequestCompileのコールが起こるようです。

Serial Compilation (Deprecated) を選択するとUNiagaraSystem::RequestCompileのコールは発生しません。

しかし、Serial Compilation (Deprecated) は Deprecatedと書かれています。

よって、既定値のAsync System Compilationを維持することが望ましいと考え、

fx.Niagara.OnDemandCompileEnabled 0でスポーン時のヒッチを回避する運用を試みています。

しかし、遅延実行されていたUNiagaraSystem::RequestCompileはロード時間に実行され、ロード時間が長くなっています。

Serial Compilation (Deprecated)の場合にはUNiagaraSystem::RequestCompileが呼ばれることはないので、同様にAsync System CompilationがUNiagaraSystem::RequestCompileを呼ばないように修正されることを希望しておりますが、今後 Async System Compilation が将来のバージョンで修正される予定はございますでしょうか。

予定がございましたらおおよその時期、なければ望ましい回避方法などを教えていただけると幸いです。

再現プロジェクトと、各設定におけるUnreal Insightのutraceを添付いたしました。

ご確認のほど、よろしくお願いいたします。

[Attachment Removed]

再現手順
UE5.6、UE5.7のプロジェクトが入っています。

また、それぞれ各条件下のutraceを添付しました。UNiagaraSystem::RequestCompileをTimersタブで検索するとその存在を確認できます。

プロジェクトを開き、PIEを実行すると3秒の待機後にNiagara Systemがスポーンされます。

スポーンされるNiagara Systemは、多数のエミッタを含み、

UNiagaraSystem::RequestCompile がコールされると 100ms 以上待機させられます。

Serial Compilation (Deprecated)に変更した場合、もしくはfx.Niagara.OnDemandCompileEnabledをDefaultEngine.iniから0にした場合

スポーン時のUNiagaraSystem::RequestCompile コールは発生しません。

なお、Serial Compilation(Deprecated) に設定してもスポーン時にUNiagaraSystem::RequestCompile がコールされる場合があることを確認しています。

これは、確認した限りでは過去のバージョンで作ったNiagara Systemを実行した時に発生するようです。

それを防ぐため、再現プロジェクトはそれぞれ当該のバージョンですべてのファイルを保存しなおしています。

Niagara Systemを保存しなおした条件下では、 Serial Compilation(Deprecated) 時はUNiagaraSystem::RequestCompileが呼ばれませんでした。

fx.Niagara.OnDemandCompileEnabledを0とした場合も、Serial Compilation(Deprecated)とAsync System Compilationに差異があります。

差異をたどると、UNiagaraSystem::UpdateSystemAfterLoad → FVersionedNiagaraEmitterData::AreAllScriptAndSourcesSynchronized → UNiagaraScript::AreGpuScriptsCompiledBySystem → CompilationMode == ENiagaraCompilationMode::AsyncTasks によって戻り値に差が生じ、

最終的にUNiagaraSystem::RequestCompileがコールされるかどうかが決まっていました。

[Attachment Removed]

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

非常に使いやすい再現用のプロジェクトをご提供いただき、誠にありがとうございました。大変助かりました。

今回ご相談いただいたことの問題は、「再現手順」で追跡されておられました通り、GPUパーティクルを持つエミッタがPIE初回起動直後のUpdateSystemAfterLoad()のタイミングでは処理に必要なシェーダを持ち合わせておらず、ほぼ必ず FVersionedNiagaraEmitterData::AreAllScriptAndSourcesSynchronized() が false を返してしまう点にあるようです。これにより、焦点がシェーダ(シェーダマップ)の準備にあるにも関わらず、Niagaraシステム全体で再コンパイルが行われてしまい、ヒッチの原因となっておりました。

コンソール変数 fx.Niagara.GpuScriptsCompiledBySystem を 0 に設定いただくと、コンパイルの必要性のチェックからGPU周りを外すことができ、(Niagaraシステムが事前にコンパイル&保存済みであれば)、結果としてRequestCompileの呼び出しを回避することができます。一度お試しください。

なお、社内の情報を調べた限りでは、一部のシチュエーションにおいて、事前にコンパイル済みであるはずの Niagara システムが FVersionedNiagaraEmitterData::AreAllScriptAndSourcesSynchronized() で false を返し、再コンパイルを誘発してしまう問題は把握されており、ToDoにあがっているようでした。ただし、こちらは必ずしも対応をお約束できるものではなく、対応時期も不明というステータスです。そのため、

・fx.Niagara.OnDemandCompileEnabled を変更してヒッチのタイミングをずらす

・Compilation Mode を Serial Compilation (Deprecated) に変更する

・fx.Niagara.GpuScriptsCompiledBySystem を 0 に設定する

といった方法が、ワークアラウンドとしてやはり挙げられておりました。

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

[Attachment Removed]

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

早速 fx.Niagara.GpuScriptsCompiledBySystem を試させていただきました。

fx.Niagara.GpuScriptsCompiledBySystem を 0 にしたところ、確かに解決するNiagara Systemがある一方、ご確認いただいた通り再コンパイルを誘発してしまうNiagara Systemもあることが確認できました。

スポーン時のその分岐は、

UNiagaraComponent::ActivateInternal > #if WITH_EDITOR > HasOutstandingCompilationRequests > NeedsRequestCompile

の戻り値によっており、再コンパイルに繋がっていました。

fx.Niagara.GpuScriptsCompiledBySystem 0 と、Serial Compilation (Deprecated) は、

確認した限りにおいては同一の要因(NeedsRequestCompile)に起因し、再コンパイルが発生するようにも見えました。

そして、この条件に該当するNiagara Systemをローカルで再保存すると再コンパイルが発生しなくなるようでした。

ただし、はっきりした条件がわからなかったので、これで運用してよいか決めかねています。

以下について、ご回答いただけると幸いです。

1. 現時点でおすすめの設定をご教示いただけませんでしょうか。直近のリリースを予定していない中長期の開発を想定し、ご提示いただけると幸いです。

2. fx.Niagara.GpuScriptsCompiledBySystem 0 は特に知られた副作用はございますでしょうか。

3. fx.Niagara.GpuScriptsCompiledBySystem 0 を設定した場合、アセットの再保存は再コンパイルの抑制として有効でしょうか。

(特に、特定環境に依存せずにRequestCompileが抑制されるのかが気になっています)

4. もしよろしければ、中規模以上または長期プロジェクトでは、一般的にどのような設定値もしくは運用方針を取ることが多いかご教示いただけませんでしょうか。 公開可能な範囲で構いませんので、推奨されるアプローチの方向性をご教示ください。

関連するCVarやプロジェクト設定が複数であり、今後のエンジンの刷新もあると思いましたので、

現時点でそれぞれをどう組み合わせて設定するのが望ましいかは、お伺いするのが確実と考えました。

お手数をおかけいたしますが、よろしくお願いいたします。

[Attachment Removed]

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

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

> fx.Niagara.GpuScriptsCompiledBySystem を 0 にしたところ、確かに解決するNiagara Systemがある一方、ご確認いただいた通り再コンパイルを誘発してしまうNiagara Systemもあることが確認できました。

(略)

> そして、この条件に該当するNiagara Systemをローカルで再保存すると再コンパイルが発生しなくなるようでした。

Niagaraシステムはエミッタやモジュールを参照する際に、単純にアセットのパスのみを保持するのではなく、そのバージョンやコンパイル条件を符号化した付随情報を一緒に保持しています。そのため、参照先のバージョンが変更されていたり、より新しいエンジンでコンパイルされたなどの条件下では、「念のためNiagaraシステムを最新状態にリフレッシュする」という意図で NeedsRequestCompile() が true を返し、コンパイルが誘発される場合があります。

これを抑制するにはエディタ上であらかじめ(できればコンパイルを行ったうえで)再保存することが有効です。

また、Niagaraシステムを指定して、そのシステムが依存するすべてのアセットを追跡してまとめてリフレッシュを行うことで、ロード時のコンパイルを抑制する「fx.PreventSystemRecompile」というコンソールコマンドがありますので、必要に応じてご利用ください。

(これをプロジェクトのNiagaraシステムすべてに対して行う「fx.PreventAllSystemRecompiles」というコマンドもあります)

詳細はこちらのリファレンスもご確認ください:

https://dev.epicgames.com/documentation/ja-jp/unreal-engine/unreal-engine-console-commands-reference

ただし、前回のご返信で申し上げましたように、現在のUE5.7の実装ではGPUスクリプトの実行に必要なリソースであるシェーダマップのロードが行われていないタイミングで NeedsRequestCompile() を走らせてしまうため、必ず true 判定が下りるという不具合があります。

そのため、ご提供いただいた再現プロジェクトでは fx.PreventSystemRecompile を使用してもロード時再コンパイルを防ぐことができません。大変恐縮ながら、現時点では「fx.Niagara.GpuScriptsCompiledBySystem 0 」との組み合わせが必須となります。

以下、頂戴した4つの質問への回答となります。

> 1. 現時点でおすすめの設定をご教示いただけませんでしょうか。直近のリリースを予定していない中長期の開発を想定し、ご提示いただけると幸いです。

今回頂戴した再現プロジェクトのケースを見る限りでは、Niagaraシステムロード時の誤った NeedsRequestCompile() = true 判定を避けることができないため、現時点では、「 fx.Niagara.GpuScriptsCompiledBySystem」を 0 に設定して運用いただくのがおすすめとなります。

この不具合に関しては社内で確認のうえ、バグトラッカーに起票する予定です。

また、Niagaraシステムは、依存しているエミッタが新しくなれば再コンパイルが必要となりますので、ロード時コンパイルが目立つNiagaraシステムについては再保存や「fx.PreventSystemRecompile」を使用して最新の状態にリフレッシュするよう心掛けていただくと、ロード時コンパイルの乱発が抑えやすくなるかと思います。

> 2. fx.Niagara.GpuScriptsCompiledBySystem 0 は特に知られた副作用はございますでしょうか

このCVarを 0 に設定しますと、GPUスクリプトのコンパイルがシステムロード時ではなく、そのGPUスクリプトを使用するエミッタの初使用時まで延期されます。これにより一般に、ロード時に集中的に現れるヒッチのタイミングを、分散させる効果が期待できます。副作用としては、この処理タイミング変更により、かえってヒッチが悪目立ちしてしまうケースがあることが挙げられます。

(2026/03/19 追記)また、CPU側(VMスクリプト)とGPUスクリプトのコンパイルが同時並行で行われなくなるため、Niagaraシステムのコンパイルにより時間がかかるようになり、VFXアーティストのイテレーションが多少悪化する恐れがあります。​

ただし、再現プロジェクトのケースでは処理タイミングをずらすことでシェーダマップのロードが間に合い、NeedsRequestCompile() が論理的に正しい判定を行えるようになって、再コンパイル(=ヒッチ)自体を回避できているようでした。

> 3. fx.Niagara.GpuScriptsCompiledBySystem 0 を設定した場合、アセットの再保存は再コンパイルの抑制として有効でしょうか。

> (特に、特定環境に依存せずにRequestCompileが抑制されるのかが気になっています)

はい、(GpuScriptsCompiledBySystem の設定値いかんに関わらず)アセットの再保存は再コンパイルの抑制として有効です。必要に応じてコンソールコマンド「fx.PreventSystemRecompile」もご利用ください。

> 4. もしよろしければ、中規模以上または長期プロジェクトでは、一般的にどのような設定値もしくは運用方針を取ることが多いかご教示いただけませんでしょうか。 公開可能な範囲で構いませんので、推奨されるアプローチの方向性をご教示ください。

基本的にデフォルトの設定がおすすめの設定となります。弊社の内製タイトルで、大規模かつ非常に長期にわたって運営しているタイトルがあるのですが、こちらのプロジェクトでは、特に設定値を変更せず、エンジンデフォルトの設定で開発を行っております。

また、再コンパイルの問題がないとしても、ゲーム中に大規模なNiagaraシステムのロードを行うと、どうしてもヒッチの原因となるため、(マップに直置きしたNiagaraですと難しいと思いますが)可能な限りゲームの幕間で事前ロードを行う手順をとることをお勧めいたします。

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

[Attachment Removed]

詳細な情報をご提供いただき、ありがとうございました。

手法について検証を行いましたが、

fx.Niagara.OnDemandCompileEnabled=0

で、しばらく運用を行うことといたしました。

ご提案いただいた fx.Niagara.GpuScriptsCompiledBySystem=0 と fx.PreventAllSystemRecompiles を試しました。

同一PCでは確かにRequestCompileを防いでいるようでした。

しかし、再保存したuassetを異なるPCで実行した場合、RequestCompileを完全に防げないケースがあるようです。

当該ケースでは UNiagaraSystem::UpdateSystemAfterLoad で、

bSystemScriptsAreSynchronized == true

bEmitterScriptsAreSynchronized == false (RequestCompileをトリガーする)

となっていました。

それらエミッターのFNiagaraVMExecutableDataIdのCompilerVersionIDは一致するのですが、ReferencedCompileHashesに差異が発生し、結果としてRequestCompileを呼んでしまうようでした。

差し支えなければ、本件が起票されたバグトラッカーのURLをご教示いただけますでしょうか。

本件修正が含まれるエンジンバージョンが判明しました際には、改めて検証を行いたいと考えております。

お手数をおかけしますが、よろしくお願いいたします。

[Attachment Removed]

実践的な運用方法をご教示いただきありがとうございます。

大変参考になりました。

追加で確認できた点を共有いたします。

fx.Niagara.OnDemandCompileEnabled=0 を iniファイルに記述すると、クック時にGPUスクリプトが脱落するケースがあるようでした(UE5.6で確認いたしました)。

今回は主にPIEでのヒッチ回避が要件でした。

いただいた情報を総合し、基本的にエンジンデフォルトの設定で運用しつつ、

エディタ実行時にのみ動的にfx.Niagara.OnDemandCompileEnabledを0に変更する形で対応することにいたしました。

現時点では開発上の課題は解決できているため、本件は解決とさせていただきます。

ご対応いただきありがとうございました。

[Attachment Removed]

追加の有益な情報をご提供いただきありがとうございました。

fx.Niagara.GpuScriptsCompiledBySystemに関するコンパイル挙動について理解が深まりました。

ご提供いただいた内容を踏まえ、改めてプロジェクトの節目などで Niagara スクリプトの再保存を行い、実際のオーバーヘッドや挙動を確認いたします。

お忙しい中、丁寧にご対応いただきありがとうございました。

[Attachment Removed]

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

ご返信が遅れて申し訳ございません。

> ご提案いただいた fx.Niagara.GpuScriptsCompiledBySystem=0 と fx.PreventAllSystemRecompiles を試しました。

> 同一PCでは確かにRequestCompileを防いでいるようでした。

> しかし、再保存したuassetを異なるPCで実行した場合、RequestCompileを完全に防げないケースがあるようです。

こちらですが、コマンドで指定したNiagaraシステムが依存するアセットを追跡して再コンパイルを行う関係上、エンジンアセットまで遡って変更が入るため、エンジンアセットもサブミットしていただく必要があります。関係するNiagaraアセットのハッシュ(ReferencedCompileHashes)、DDCキャッシュ、ビルドバージョン(CompilerVersionID)の条件がすべてそろえば、「あるPCで防げたRequestCompile」は「別のPC」でも防げるはずです。

この観点でいえば、乱発できないコマンドであることは確かです。今回改めて内製プロジェクトのVFXチームにもワークフローの確認を行いましたが、大型長期プロジェクトではVFXアセット数も数千では済まない量となることから、再コンパイルの問題を完全に押さえ込むことは現実的ではなく、

  • DDC不足によるコンパイルはDDCが構築されるのを待つ(避けられない)
  • ハッシュ違いによる再コンパイルは、気付いた範囲で再保存を行う

という形での作業にならざるを得ないとのことでした。

> 差し支えなければ、本件が起票されたバグトラッカーのURLをご教示いただけますでしょうか。

> 本件修正が含まれるエンジンバージョンが判明しました際には、改めて検証を行いたいと考えております。

こちらの「シェーダーマップの構築遅れによる再コンパイル誤判定の疑い」についてですが、先日は起票する方針であるとご報告しましたが、PSOプリキャッシュ絡みの「制約」である可能性があり、現在は起票を保留して開発側と確認を行なっている最中となっております。進捗があり次第、改めてご報告させていただきます。

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

[Attachment Removed]

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

ご返信が遅くなり、申し訳ございません。

基本的にエンジンデフォルトの設定で運用しつつ、エディタ実行時にのみOnDemandCompileEnabledを0に変更するとのこと、承知いたしました。

以下、本スレッドで挙げられたオプションのうち、技術的な詳細が把握できたものについて改めてまとめ直しました。既出の情報も多いですが、ご容赦ください。

■fx.Niagara.GpuScriptsCompiledBySystem の役割

現在の標準のNiagaraコンパイル方式である「Async System Compilation」は、Niagaraのコンパイルを非同期で行う点を特徴としております。ただし、非同期で行われるそれぞれのコンパイル単位で行われる内容は、「CPU側(VMスクリプト)」をコンパイルし、続いて「GPU側(GPUスクリプト)」をコンパイルするという、ある意味でシーケンシャルなものでした。

この順序だてたコンパイルによる、エディタ上でのVFXアーティストのイテレーションの悪さや、クックの効率悪化を改善するために、後から「fx.Niagara.GpuScriptsCompiledBySystem」オプションが導入された形です。

この値が「1」のとき(現在はデフォルト)、従来順番に行っていたVMスクリプトとGPUスクリプトのコンパイルが同時並列で行われるようになり、Niagaraにかかるコンパイル時間が大きく改善されます。特にNiagaraエディタ上の作業に恩恵があります。

■コンパイルの有無について

①GpuScriptsCompiledBySystem =1の場合、

非常に早期にコンパイル判定を行うため、非クック環境下では GameThreadShaderMap の準備が間に合っておらず、これが遠因で、本スレッド既出のとおり bEmitterScriptsAreSynchronized = false に繋がり、Niagaraシステムの再コンパイルが決定します。

ただし、事前にNiagaraスクリプトの保存などの処置を行っていれば、実際にはコンパイル処理タスクは起動されず、DDCからキャッシュを取得して終わりになります。ログなどを見ていると毎回コンパイルが発生しているように見えるのですが、実際には RequestCompile をトリガーとしてDDCキャッシュを回収にいくという動きをし、この一連のプロセスにかかった時間をコンパイルタイムとして報告します。

言い換えますと、”本当の意味でのコンパイル”は行われないが、コンパイルの一部のコードパスが利用され、相応のオーバーヘッドもある、という設定になります。

②GpuScriptsCompiledBySystem = 0 の場合、

NiagaraはVMスクリプトの準備を先に行い、続いて直接DDCを確認します。①と比較してNiagaraが利用可能になるまでの時間はかかりますが、DDCキャッシュが拾える状況では、コンパイルは行われません。

①も②もDDCからキャッシュを拾える局面では最小限の時間でNiagaraを準備しますが、②のほうが処理が時間で分散しており、実行環境によってはヒッチを押さえやすいかと思います。ただし、①と②ではDDCのキーが異なるため、①で作業してPIE時に②に動的に切り替えるアプローチをとりますと、キャッシュミスが発生し、本当のコンパイルが誘発される恐れがあります。そのため、プロジェクトを通じて、一貫して値を固定しておくのが無難です。

> 現時点では開発上の課題は解決できているため、本件は解決とさせていただきます。​

それではお言葉に甘えて本スレッドはクローズさせていただきますが、もし今回の回答で新たな不明点が生じた場合は、遠慮なくコメントをご追記ください。

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

[Attachment Removed]