Crash on new game load because Unreal's renderer keeps a leftover pointer to a garbage collected asset

Summary

Intermittent EXCEPTION_ACCESS_VIOLATION during map travel: FSceneViewState::ExposureCompensationCurveLUT.Curve_GameThread is a raw UCurveFloat* that is neither GC-referenced nor weak

What Type of Bug are you experiencing?

Rendering (Graphics / Niagara)

Steps to Reproduce

Steps

  1. Use an editor-binary build (UnrealEditor.exe, e.g. launched with -Game). The affected member is inside #if WITH_EDITORONLY_DATA (Engine/Source/Runtime/Renderer/Private/ScenePrivate.h:934), so packaged builds compile the path out.

  2. In a level, set an Auto Exposure Bias Curve (Exposure Compensation Curve) on any post-process source; a PostProcessVolume or a PostProcessComponent. In our project it is Ultra Dynamic Sky’s Exposure_Compensation_Curve on its PostProcessComponent.

  3. Ensure nothing outside the level references the curve asset (typical when only the level’s sky/post-process actor uses it).

  4. Render at least one frame so FSceneViewState caches the raw curve pointer (FSceneViewState::SetExposureCompensationCurve stores Curve_GameThread).

  5. Trigger a same-map travel with UGameplayStatics::OpenLevel.

(Repeat steps 4–5 several times until crash).

####Suspected root cause:

Curve_GameThread is a raw UCurveFloat* that is neither registered with the GC (FSceneViewState::AddReferencedObjects collects only MIDPool) nor weak, and the view state lives on the persistent ULocalPlayer, so it survives travel. When the world dies during travel, nothing references the curve any more and GC can collect it while the stale pointer remains cached.

The mid-LoadMap viewport redraw (UnrealEngine.cpp:16294 → FSceneView::EndFinalPostprocessSettings, SceneView.cpp:2371) then calls SetExposureCompensationCurve with the freshly loaded (different) pointer, and PostProcessEyeAdaptation.cpp:728 calls OnUpdateCurve.Remove() on the freed object.

Crash frequency depends on allocator reuse of the freed block (~50% of loads in our project).

####Suggested Fix:
Make Curve_GameThread a TWeakObjectPtr (skip Remove() when stale) or register it in AddReferencedObjects

Expected Result

Travel completes normally; the view state re-caches the newly loaded exposure compensation curve.

Observed Result

Intermittent EXCEPTION_ACCESS_VIOLATION reading 0xffffffffffffffff in UnrealEditor-Renderer during LoadMap (inside FSceneViewState::SetExposureCompensationCurve → OnUpdateCurve.Remove() on the freed UCurveFloat). When the freed allocation has been reused, it can also surface as bogus MTAccessDetector “race detected” ensures reporting implausible reader counts (~400k).

Affects Versions

5.7

Platform(s)

Windows

For crash reports, include your callstack

EXCEPTION_ACCESS_VIOLATION reading address 0xffffffffffffffff

UnrealEditor_Renderer!TMulticastDelegateRegistration<void __cdecl(UCurveBase *,unsigned int),FDefaultDelegateUserPolicy>::Remove() [Engine\Source\Runtime\Core\Public\Delegates\DelegateSignatureImpl.inl:1008] UnrealEditor_Renderer!FSceneViewState::SetExposureCompensationCurve() [Engine\Source\Runtime\Renderer\Private\PostProcess\PostProcessEyeAdaptation.cpp:729] UnrealEditor_Engine!FSceneView::EndFinalPostprocessSettings() [Engine\Source\Runtime\Engine\Private\SceneView.cpp:2375] UnrealEditor_Engine!ULocalPlayer::CalcSceneView() [Engine\Source\Runtime\Engine\Private\LocalPlayer.cpp:989] UnrealEditor_Engine!UGameViewportClient::Draw() [Engine\Source\Runtime\Engine\Private\GameViewportClient.cpp:1687] UnrealEditor_Engine!FViewport::Draw() [Engine\Source\Runtime\Engine\Private\UnrealClient.cpp:1798] UnrealEditor_Engine!UGameEngine::RedrawViewports() [Engine\Source\Runtime\Engine\Private\GameEngine.cpp:789] UnrealEditor_Engine!UEngine::LoadMap() [Engine\Source\Runtime\Engine\Private\UnrealEngine.cpp:16294] UnrealEditor_Engine!UEngine::Browse() [Engine\Source\Runtime\Engine\Private\UnrealEngine.cpp:15420] UnrealEditor_Engine!UEngine::TickWorldTravel() [Engine\Source\Runtime\Engine\Private\UnrealEngine.cpp:15618] UnrealEditor_Engine!UGameEngine::Tick() [Engine\Source\Runtime\Engine\Private\GameEngine.cpp:1872] UnrealEditor!FEngineLoop::Tick() [Engine\Source\Runtime\Launch\Private\LaunchEngineLoop.cpp:5828] UnrealEditor!GuardedMain() [Engine\Source\Runtime\Launch\Private\Launch.cpp:190]