ULevelStreamingDynamic::LoadLevelInstanceの挙動について質問

OnLevelLoadedでサブレベルのロードがすべて完了していたものと想定して

LoadedStreamingLevel->SetShouldBeVisible(true)を呼び出して表示したが、その後

エンジン内部でUWorldPartitionLevelStreamingDynamic::Activate()が呼ばれ、FlushAsyncLoading(強制同期ロード処理)が呼ばれることによりゲームが数秒固まってしまった。

<br/>

本来は、ロードから表示まで メインのゲームスレッドを止まらせることなく全て非同期で行いたいが、これを実現するために今の処理をどのように変更すればよいか教えていただけますでしょうか

<br/>

また、そもそもULevelStreamingDynamic::LoadLevelInstanceというAPIは非同期ロードをしたい場合に使用するAPIとしてふさわしいのかも教えていただきたいです。

<br/>

[Attachment Removed]

FlushAsyncLoadingは UWorldPartitionLevelStreamingDynamic::RequestLevel 関数内から呼び出されたものでよろしいでしょうか?

また読み込もうとしているサブレベルはWorldPartition化されたLevelでサブワールドパーティションとしてワールドに配置しようとしていると推測していますが間違いございませんか?

周辺の情報についてもう少し詳しく頂戴できるとありがたいです。

加えまして一つお伺いしたいのですが、ワールドパーティションレベルではサブレベルの制御と近しい機能としてデータレイヤーがありますが、LoadLevelInstanceを使っているのはなにか動的に配置する必要があるためでしょうか?

[Attachment Removed]

>>FlushAsyncLoadingは UWorldPartitionLevelStreamingDynamic::RequestLevel 関数内から呼び出されたものでよろしいでしょうか?

こちらは、OnLevelLoadedでロード完了後にSetShouldBeVisible(true)を呼び出した後にUWorldPartitionLevelStreamingDynamic::Activate()から呼ばれた後にFlushAsyncLoadingが呼ばれておりました

>サブレベルはWorldPartition化されたLevelでサブワールドパーティションとしてワールドに配置しようとしていると推測していますが間違いございませんか?

こちらとしては、サブレベルのように運用したいと思っております。

>ワールドパーティションレベルではサブレベルの制御と近しい機能としてデータレイヤーがありますが、>LoadLevelInstanceを使っているのはなにか動的に配置する必要があるためでしょうか?

はい、こちらですがメインのパーシスタントレベル内で、動的にサブレベルのデータを切り替える運用のもと設計してあるため、LoadLevelInstanceを使用しております。

今回の目的としましては、OnLevelLoadedがよばれたときはサブレベルの全てのデータを非同期ロードが完了して、あとはサブレベルを表示するという事が達成したいです。

[Attachment Removed]

大変お待たせしております。

>>FlushAsyncLoadingは UWorldPartitionLevelStreamingDynamic::RequestLevel 関数内から呼び出されたものでよろしいでしょうか?

> こちらは、OnLevelLoadedでロード完了後にSetShouldBeVisible(true)を呼び出した後にUWorldPartitionLevelStreamingDynamic::Activate()から呼ばれた後にFlushAsyncLoadingが呼ばれておりました

実際にFlushAsyncLoadingが呼び出されているコールスタックを確認させていただきたくてご質問させていただきました。

UWorldPartitionLevelStreamingDynamic::Activateではストリーミングレベルを追加するだけでFlushAsyncLoadingは

ULevelStreaming::UpdateStreamingState > UWorldPartitionLevelStreamingDynamic::RequestLevel > FlushAsyncLoading という経路呼ばれたと推測しております。間違いないでしょうか?

このパスでブロッキングが起きる場合恐らく「サブレベル」にAlwaysLoaded のアクターがふくまれているためブロッキングが起きていると思われます。

「サブレベル」のEnableStreamingフラグや各アクターのIsSpagtiallyLoadedの状態を確認いただいてもよいでしょうか?

ロード待ちを行うためには「サブレベル(サブワールドパーティションとして動的にロードされるレベルインスタンス)」に対してデータレイヤーを割り当ててターゲットステートをロードにしておく > ロード完了を待ってデータレイヤーをActivate という形が良いかと思いますが、「サブレベル」は無数にバリエーションがあってかつ瞬時に表示状態に移行する必要性がございますか?

[Attachment Removed]

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

>ULevelStreaming::UpdateStreamingState > UWorldPartitionLevelStreamingDynamic::RequestLevel > FlushAsyncLoading という経路呼ばれたと推測しております。間違いないでしょうか?

はいあっております。スタックトレースを添付します

UnrealEditor-Engine.dll!UWorldPartitionLevelStreamingDynamic::RequestLevel(UWorld * InPersistentWorld, bool bInAllowLevelLoadRequests, ULevelStreaming::EReqLevelBlock InBlockPolicy) 行 337
	: G:\Project\sandbox\GitLab\ue5.7.4_edit\Engine\Source\Runtime\Engine\Private\WorldPartition\WorldPartitionLevelStreamingDynamic.cpp(337)
UnrealEditor-Engine.dll!ULevelStreaming::UpdateStreamingState::__l2::<lambda_1>::operator()() 行 1011
	: G:\Project\sandbox\GitLab\ue5.7.4_edit\Engine\Source\Runtime\Engine\Private\LevelStreaming.cpp(1011)
UnrealEditor-Engine.dll!ULevelStreaming::UpdateStreamingState(bool & bOutUpdateAgain, bool & bOutRedetermineTarget, const TOptional<UE::FTimeout const> & InExternalTimeout) 行 1222
	: G:\Project\sandbox\GitLab\ue5.7.4_edit\Engine\Source\Runtime\Engine\Private\LevelStreaming.cpp(1222)
[インライン フレーム] UnrealEditor-Engine.dll!FStreamingLevelPrivateAccessor::UpdateStreamingState(ULevelStreaming *) 行 808
	: G:\Project\sandbox\GitLab\ue5.7.4_edit\Engine\Source\Runtime\Engine\Classes\Engine\LevelStreaming.h(808)
UnrealEditor-Engine.dll!UWorld::UpdateLevelStreaming(const TOptional<UE::FTimeout const> & ExternalTimeout) 行 5045
	: G:\Project\sandbox\GitLab\ue5.7.4_edit\Engine\Source\Runtime\Engine\Private\World.cpp(5045)
UnrealEditor-Engine.dll!UGameViewportClient::Draw(FViewport * InViewport, FCanvas * SceneCanvas) 行 1848
	: G:\Project\sandbox\GitLab\ue5.7.4_edit\Engine\Source\Runtime\Engine\Private\GameViewportClient.cpp(1848)
UnrealEditor-Engine.dll!FViewport::Draw(bool bShouldPresent) 行 1798
	: G:\Project\sandbox\GitLab\ue5.7.4_edit\Engine\Source\Runtime\Engine\Private\UnrealClient.cpp(1798)
UnrealEditor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) 行 2440
	: G:\Project\sandbox\GitLab\ue5.7.4_edit\Engine\Source\Editor\UnrealEd\Private\EditorEngine.cpp(2440)
UnrealEditor-UnrealEd.dll!UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode) 行 537
	: G:\Project\sandbox\GitLab\ue5.7.4_edit\Engine\Source\Editor\UnrealEd\Private\UnrealEdEngine.cpp(537)

>>「サブレベル」のEnableStreamingフラグや各アクターのIsSpagtiallyLoadedの状態を確認いただいてもよいでしょうか?

確認してみます。

>>無数にバリエーションがあってかつ瞬時に表示状態に移行する必要性がございますか?

現状は、バリエーションを瞬時に切り替える必要はありません

こちらで問題の切り分けもしたいため、今一度確認させてください ULevelStreamingDynamic::LoadLevelInstanceというAPIの処理はレベルインスタンスないしレベルを非同期でロードされているという認識であっておりますでしょうか

[Attachment Removed]

> >>無数にバリエーションがあってかつ瞬時に表示状態に移行する必要性がございますか?

> 現状は、バリエーションを瞬時に切り替える必要はありません

失礼しました。少し文脈に誤りがありました。

ワールドに複数の「サブレベル」を呼び込むことはあるかどうかを伺いたいです。

「サブレベル」にデータレイヤーを割り当ててデータレイヤーの制御でロード待ちと表示を制御することは恐らく解法の一つになりますが、

複数の動的なサブレベルを呼び込む場合それぞれのサブレベルに固有のデータレイヤーを持たせることになってしまいます。

またIsSpatiallyLoadedがfalseの本来AlwaysLoadedに含まれるアクターもデータレイヤーに属させることでこのブロッキングロードの有無の判定を迂回できます。

>> ULevelStreamingDynamic::LoadLevelInstance はレベルインスタンスないしレベルを非同期でロードするか

はい。ULevelStreamingDynamic::LoadLevelInstanceはワールドにストリーミングレベルを登録しストリーミングを行いますがこの対象にワールドパーティションレベルの内容物としてのワールドパーティションセルはこの時点では登録されておりません。セルの登録は外側のレベルの表示タイミングで行われます。またOnLevelLoadedはこの外側のレベル部分だけのロード終了を通知しているだけのはずです。

新たに登録されたサブレベルの内容物のセルも非同期ロードされますが、AlwaysLoadedのセルだけはブロッキングロードされてしまうためこのような現象が起きていると推測しています。

[Attachment Removed]

>ワールドに複数の「サブレベル」を呼び込むことはあるかどうかを伺いたいです。

はい、現状よみこむサブレベルは一つだけとなり一つのサブレベル内でデータレイヤーを用いて

状況によって、一部のActorの表示切り替えを行っております。

>IsSpatiallyLoadedがfalseの本来AlwaysLoadedに含まれるアクターもデータレイヤーに属させることでこのブロッキングロードの有無の判定を迂回できます。

ありがとうございます。これらのデータの存在を初めて知ったのですが

AlwaysLoadedのセルにあるActorというのはエディターのどこを確認すればよいか教えていただけないでしょうか

また解決策としてはIsSpatiallyLoadedがfalseのものをデータレイヤーに属するようにすれば避けられるという事で認識はただしいでしょうか

[Attachment Removed]

追加の質問で恐縮ですが

>OnLevelLoadedはこの外側のレベル部分だけのロード終了を通知しているだけのはずです。

上記のように回答していただいたのですが、内側のロード終了を通知するものはありますでしょうか

[Attachment Removed]

各セル個別にロードの通知はありません。

ストリーミングソースのTargetBehaviorでステートを指定し、任意のタイミングでFWorldPartitionStreamingQuerySource を UWorldPartitionSubsystem::IsStreamingCompleted に渡してクエリするという方法が利用できます。

[Attachment Removed]

ありがとうございます。

つまり、ActorのIsSpatiallyLoadedをtrueにすればAlwaysLoadedのセルに含まれず

ブロッキングロードはされなくなるという認識で問題ないでしょうか

[Attachment Removed]

はい。そのはずです。ただしどのような構成なのかつかみ切れていない部分があるので、お試しいただいて改善がみられるかご共有いただけますと幸いです。

[Attachment Removed]

ありがとうございます。教えていただいたフラグを制御することで ブロックしない事が確認できました。

最後に一点だけ

レベル内の一部Actor DirectionaLightなどは このフラグを制御することができないのですが

これは、仕様ということでこのようなActorの場合はデータレイヤーに逃がすという対象方法でよろしいのでしょうか

[Image Removed]

[Attachment Removed]

はいそのデータレイヤーに逃がす方法でブロッキングロードを迂回することが出来ます。

ADirectionalLight及びDirectionalLightComponentは必ずワールド全体を覆う大きさのバウンズが適用されます。このような空間ロードする意味がないアクターはIsSpatiallyLoadedのフラグの制御が無効化されます。

見当違いかもしれませんが、もしDirectionalLightなどがレベルの編集用の用途で存在しランタイムでライティングに影響させないようなものであれば、ライトに対して Is Main World Only フラグを付与し てLevelInstanceとして展開されるときにアクターを除外することも検討できます。

[Attachment Removed]

データレイヤーに逃す方法でブロッキングロードが回避できることが確認できました。

こちらで既存ワークフローに組み込めるか試してみたいとおもいます。

教えていただきありがとうございました。本件クローズをお願い致します。

[Attachment Removed]