Crash from left-over post process material instance pointing to an invalid texture resource

Hello

we are investigating a render thread crash which happens when a player loads a savegame in our game and then changes the video settings in our in-game settings.

The actual path of the crash is curvy so I’ll try to lay it out as good as possible while focussing on common Unreal code.

The crash happens because a dynamic material instance created by View.State->GetReusableMID() in UMaterialInterface::OverrideBlendableSettings() survives a world change while still referencing a world-bound texture resource leading to a dangling pointer.

The call to OverrideBlendableSettings comes from one of our post process material which we dynamically assign and control and which references a scene capture render texture within the world. It is already a dynamic material instance when we assign it to the camera component withAddOrUpdateBlendable() but the engine creates another copy of it nonetheless with the mentioned GetReusableMID() call.

When changing the video settings the render thread eventually calls FUniformExpressionSet::FillUniformBuffer and crashes when reaching the validity checks in the “// Cache 2D texture uniform expressions.” section because of trying to access this invalid texture resource.

const UTexture* Value = nullptr;
GetTextureValue(EMaterialTextureParameterType::Standard2D, ExpressionIndex, MaterialRenderContext,MaterialRenderContext.Material,Value);
if (Value)
{
}

Value is recognised as true but the following Value->IsValidLowLevel() returns “Warning: Other object in slot”

Therefore the ensureMsgf below “// gmartin: Trying to locate UE-23902” fires

and the following checkf() below “// Trying to track down a dangling pointer bug.” crashes when trying to access Value.

The reason this dynamic material instance survives is because the only time the MIDPool is cleared is in ULocalPlayer::CleanupViewState() which is called in UEngine::LoadMap() which is NOT called when using a World->ServerTravel() > World->SeamlessTravel().

And we are using seamless travel.

The crash happens only in a shipping or debug build and not in the editor.

This is probably because we use no seamless travel in the editor PIE.

When we manually call CleanupViewState() before loading:

  • the dynamic material instance is cleared and
  • the crash does not happen.

I’m wondering if the view state is supposed to survive with a seamless travel or if it is a bug / uncovered edge case.

[Attachment Removed]

Hello, thanks for reporting this detailed report of the crash. I’m not sure the intention of the seamless travel (not a system I’m familiar with), but it does appear that you’ve found a bug. My assumption is that the MIDPool should be cleared especially if we have dangling pointers.

I’m not sure the side effects of calling CleanupViewState() (what other state might it be clearing?).

I’ll be on vacation for the next week, so I’m forwarding this ticket along to a colleague. Most likely we just need to make a bug report for this issue, since it sounds like you have a workaround.

[Attachment Removed]

Hi Jason, thank you for your reply.

I’m with you about being unsure if calling CleanupViewState() could have some side effects - or maybe calling JUST CleanupViewState() is not enough and there still remain some other elements which can cause issues down the line.

Therefore I would wait for more info from Epic - at least for the moment.

When we send out a new patch for our game in a few weeks we’ll use the best fix at hand at that time.

[Attachment Removed]

Are there any update to this? A bug ticket or something like that?

[Attachment Removed]

Hi everyone,

since we haven’t heard anything in a while, I just wanted to check, if we can help more on this issue - either with more info or repro steps? Maybe you have had time to take a look at it?

[Attachment Removed]

Greetings,

It does make sense to clean up the MIDPool when changing levels through SeamlessTravel, as this is analogous to changing the level via LoadMap. Nice find! The cached MIDs are just an optimization to avoid recreating them every frame, and so I don’t think any information is lost by cleaning them up, even if done unnecessarily. Calling it where NotifyLoadedWorld is called in FSeamlessTravelHandler::Tick seems like one logical place to add it, although I need to consult with people on the networking team as to whether that’s the correct spot. Also, it makes sense for us to consider whether other logic present in UEngine::LoadMap also needs to be mirrored during seamless travel. The fix should be present in UE 5.8.

Cheers!

--Jason Hoerner

[Attachment Removed]