Description

If sublevels containing multiple reflection captures are loaded in consecutive ticks and the execution timing of UpdateMaxCubemaps on rendering thread is delayed, UpdateAllReflectionCaptures will be executed twice on the game thread.
This will cause the ensure condition failed, and will lose some reflection capture textures.

Here is the timeline

  1. (Persistent level doesn't have any reflection capture)
  2. Load sublevels
  3. First execute UpdateAllReflectionCaptures and enqueue UpdateMaxCubemaps
  4. Next tick
  5. (UpdateMaxCubemaps is delayed in Rendring thread)
  6. Because ReflectionSceneData.CubemapArray.CubemapSize is not set yet, Execute UpdateAllReflectionCaptures and enqueue UpdateMaxCubemaps again
  7. Execute UpdateMaxCubemaps on rendering thread and update ReflectionSceneData.CubemapArray.CubemapSize
Steps to Reproduce
  1. Apply the following patch to the engine to easily reproduce the issue
  2. Open [Link Removed] on 4.26
  3. Package the project for Win64
  4. Launch the packaged game
  5. Press 1 key

Result:

Ensure occurs and probably lose some reflection capture textures.

Reflection capture /Game/Levels/Sublevel_4_LevelInstance_5.Sublevel_4:PersistentLevel.SphereReflectionCapture_1.NewReflectionComponent uploaded twice without reloading its lighting scenario level.  The Lighting scenario level must be loaded once for each time the reflection capture is uploaded.

 

The patch in FScene::AllocateReflectionCaptures. it applies additional delay on the rendering thread.

			if (bNeedsUpdateAllCaptures)
			{
				ReflectionSceneData.MaxAllocatedReflectionCubemapsGameThread = DesiredMaxCubemaps;

				FScene* Scene = this;
				uint32 MaxSize = ReflectionSceneData.MaxAllocatedReflectionCubemapsGameThread;
				ENQUEUE_RENDER_COMMAND(ResizeArrayCommand)(
					[Scene, MaxSize, ReflectionCaptureSize](FRHICommandListImmediate& RHICmdList)
					{
			 			FPlatformProcess::Sleep(0.1);  // <----------
						// Update the scene's cubemap array, which will reallocate it, so we no longer have the contents of existing entries
						Scene->ReflectionSceneData.CubemapArray.UpdateMaxCubemaps(MaxSize, ReflectionCaptureSize);
					});

				// Recapture all reflection captures now that we have reallocated the cubemap array
				UpdateAllReflectionCaptures(CaptureReason, bVerifyOnlyCapturing, bCapturingForMobile);
			}
Callstack

ReflectionCUpdate426-Win64-Debug.exe!FDebug::OptionallyLogFormattedEnsureMessageReturningFalseImpl() [E:\dev\UnrealEngine-4.26.1\Engine\Source\Runtime\Core\Private\Misc\AssertionMacros.cpp:497]
ReflectionCUpdate426-Win64-Debug.exe!<lambda_15e528883abd17569d2237ed7c67730a>::operator()() [E:\dev\UnrealEngine-4.26.1\Engine\Source\Runtime\Renderer\Private\ReflectionEnvironmentCapture.cpp:1443]
ReflectionCUpdate426-Win64-Debug.exe!DispatchCheckVerify<bool,<lambda_15e528883abd17569d2237ed7c67730a> >() [E:\dev\UnrealEngine-4.26.1\Engine\Source\Runtime\Core\Public\Misc\AssertionMacros.h:165]
ReflectionCUpdate426-Win64-Debug.exe!<lambda_5c2768b46f91cb866ff722fd43dfa9ba>::operator()() [E:\dev\UnrealEngine-4.26.1\Engine\Source\Runtime\Renderer\Private\ReflectionEnvironmentCapture.cpp:1443]
ReflectionCUpdate426-Win64-Debug.exe!TEnqueueUniqueRenderCommandType<`FScene::CaptureOrUploadReflectionCapture'::`8'::UploadCaptureCommandName,<lambda_5c2768b46f91cb866ff722fd43dfa9ba> >::DoTask() [E:\dev\UnrealEngine-4.26.1\Engine\Source\Runtime\RenderCore\Public\RenderingThread.h:184]
ReflectionCUpdate426-Win64-Debug.exe!TGraphTask<TEnqueueUniqueRenderCommandType<`FScene::CaptureOrUploadReflectionCapture'::`8'::UploadCaptureCommandName,<lambda_5c2768b46f91cb866ff722fd43dfa9ba> > >::ExecuteTask() [E:\dev\UnrealEngine-4.26.1\Engine\Source\Runtime\Core\Public\Async\TaskGraphInterfaces.h:888]
ReflectionCUpdate426-Win64-Debug.exe!FNamedTaskThread::ProcessTasksNamedThread() [E:\dev\UnrealEngine-4.26.1\Engine\Source\Runtime\Core\Private\Async\TaskGraph.cpp:709]
ReflectionCUpdate426-Win64-Debug.exe!FNamedTaskThread::ProcessTasksUntilQuit() [E:\dev\UnrealEngine-4.26.1\Engine\Source\Runtime\Core\Private\Async\TaskGraph.cpp:601]
ReflectionCUpdate426-Win64-Debug.exe!FTaskGraphImplementation::ProcessThreadUntilRequestReturn() [E:\dev\UnrealEngine-4.26.1\Engine\Source\Runtime\Core\Private\Async\TaskGraph.cpp:1474]
ReflectionCUpdate426-Win64-Debug.exe!RenderingThreadMain() [E:\dev\UnrealEngine-4.26.1\Engine\Source\Runtime\RenderCore\Private\RenderingThread.cpp:373]
ReflectionCUpdate426-Win64-Debug.exe!FRenderingThread::Run() [E:\dev\UnrealEngine-4.26.1\Engine\Source\Runtime\RenderCore\Private\RenderingThread.cpp:509]
ReflectionCUpdate426-Win64-Debug.exe!FRunnableThreadWin::Run() [E:\dev\UnrealEngine-4.26.1\Engine\Source\Runtime\Core\Private\Windows\WindowsRunnableThread.cpp:84]
ReflectionCUpdate426-Win64-Debug.exe!FRunnableThreadWin::GuardedRun() [E:\dev\UnrealEngine-4.26.1\Engine\Source\Runtime\Core\Private\Windows\WindowsRunnableThread.cpp:27]
ReflectionCUpdate426-Win64-Debug.exe!FRunnableThreadWin::_ThreadProc() [E:\dev\UnrealEngine-4.26.1\Engine\Source\Runtime\Core\Private\Windows\WindowsRunnableThread.h:38]
KERNEL32.DLL!UnknownFunction []
ntdll.dll!UnknownFunction []

Have Comments or More Details?

There's no existing public thread on this issue, so head over to AnswerHub just mention UE-112576 in the post.

0
Login to Vote

Unresolved
ComponentRendering
Affects Versions4.26
Target Fix5.0
CreatedApr 1, 2021
UpdatedApr 7, 2021