Crash in RemapGuid() in PIE when streaming an unsaved landscape tile

Hello,

We have a crash that’s been happening for a while, and is triggered when these conditions are met:

  1. The cvar s.UseUnifiedTimeBudgetForStreaming must be true
  2. The level must contain a landscape
  3. World partition must be enabled
  4. Some tiles of the landscape must be modified and left unsaved before starting a PIE session

When the camera comes near the unsaved tiles there is a chance that a crash occur in RemapGuid() when the tiles are streamed in.

In UWorld::UpdateLevelStreaming(), if several streaming levels become visible or invisible in the same frame the global PIEInstanceID will be set to -1 in the process (through a call to ULevelStreaming::BroadcastLevelVisibleStatus() and then SetPlayInEditorWorld()) and won’t be restored to the previous value afterward.

It’s not a problem in most cases, except when the loaded cell contains an unsaved landscape proxy actor that need a valid PIEInstanceID during the serialization.

I fixed the issue by restoring the correct PIE instance ID before each streaming level is processed:

[Image Removed]

but let me know if there is a better solution.

Have a nice day!

Steps to Reproduce

  1. Open the test project
  2. Open the “Level” asset
  3. Run the utility blueprint “UBP_Crash”
  4. Hit the “Crash” button

Hello!

Thanks for sharing the project. It made our life super simple. I was able to reproduce the problem in version 5.6 but it’s not happening in Main. We are investigating to understand what is happening as lots of assets should be getting remapped IDs.

As for your proposed fix, it seems more appropriate to use a FScopedConditionalWorldSwitcher in UEngine::HandleUnifiedStreaming. There are some BP related events that might be using that same guard. Those null the world and reset the PIE ID on destruction which can conflict with FTemporaryPlayInEditorIDOverride. The conditional switcher can properly deal when multiple embedded scopes are creatd.

	// If the process async loading did not use the full time budget it will be granted to UpdateLevelStreaming for all ticking worlds
	// If process async loading exceeded the budget - UpdateLevelStreaming will amortize the hitch
	for (int32 WorldIdx = 0; WorldIdx < WorldList.Num(); ++WorldIdx)
	{
		FWorldContext& Context = WorldList[WorldIdx];
		if (Context.World() != nullptr && Context.World()->ShouldTick())
		{
--->			FScopedConditionalWorldSwitcher WorldSwitcher(Context.World());
			Context.World()->UpdateLevelStreaming(StreamingTimeout);

Regards,

Martin

We found out why the problem was not happening in Main. It got fixed in CL#46123516. I recommend taking that fix over my previous recommendation.

Regards,

Martin

Hi Martin,

Thank you for the investigation! I’ll check the CL in main then

Have a nice day!