Render target of scene capture in main renderer does not update if Niagara system samples it

We have noticed that when a scene capture component has the ‘Render in Main Renderer’ property enabled and writes to a given render target, if the same render target is read from a Niagara system then the render target will stop updating. Disabling ‘Render in Main Renderer’ will lead to the render target updating again.

If you take a PIX capture when the problem is occurring you’ll see that the draw issued to copy the rendered image to the scene capture render target, with RDG scope ‘SceneCapturePass_SceneDepth/View’, is not present.

If a debugger is attached when the problem occurs, execution will break as an ensure will trigger (“Resource NiagaraTexture2D is in external access mode and is valid for access with the following states: SRVCompute|SRVGraphicsPixel|SRVGraphicsNonPixel, but is being used in pass Scene/CustomRenderPasses/CustomRenderPass… SceneCapturePass_SceneDepth/View… with access RTV”).

We have fixed the issue (including the ensure) on our side by making the change shown below to FSceneCapturePass::OnPreRender in SceneCaptureRendering.cpp. Note that when the Niagara system mentioned above doesn’t exist, the scene capture render target is registered with the RDG builder as an external texture, with stock code keeping it in external access mode, but when Niagara registers the render target with the RDG builder it asks the external texture to use internal access mode. Given the scene capture render target is going to be written to by that draw performing the copy mentioned above, allowing RDG to manage transitions for this resource by setting the render target to internal access mode (as Niagara does) seemed appropriate.

	virtual void OnPreRender(FRDGBuilder& GraphBuilder) override
	{
		// Resize the render resource if necessary -- render target size may have been overridden to the main view resolution, or later be changed back
		// to the resource resolution. The resize call does nothing if the size already matches.
		((FTextureRenderTarget2DResource*)SceneCaptureRenderTarget)->Resize(GraphBuilder.RHICmdList, RenderTargetSize.X, RenderTargetSize.Y, bAutoGenerateMips);

		RenderTargetTexture = SceneCaptureRenderTarget->GetRenderTargetTexture(GraphBuilder);

// BEGIN CHANGE
		GraphBuilder.UseInternalAccessMode(RenderTargetTexture);
// END CHANGE

[Attachment Removed]

Steps to Reproduce
General repro steps:

  • Have a blueprint with a scene capture component setup as follows: Capture Source set to ‘SceneDepth in R’, Capture Every Frame set to true, Texture Target pointing to a render target
  • Have a Niagara system with a GPU emitter containing a module sampling from the same render target
  • Observe said render target is not updated
    • If the debugger is attached, execution will break when an ensure with a message similar to the following fires “Resource NiagaraTexture2D is in external access mode and is valid for access with the following states: SRVCompute|SRVGraphicsPixel|SRVGraphicsNonPixel, but is being used in pass Scene/CustomRenderPasses/CustomRenderPass… SceneCapturePass_SceneDepth/View… with access RTV”

Notes about the provided project:

  • The map in the provided project contains two blueprint actors with scene captures configured as mentioned in the general repro steps and writing to different render targets, as well as one Niagara system reading from just one of the render targets.
  • If said actors are selected and moved around, you’ll see only one of the render targets is updated.
  • If the Niagara system is deleted, when both actors are selected and moved around then both render targets will be updated.
    [Attachment Removed]

Your fix makes sense -- I tested it locally, and it works fine for me as well. We’ll include it in UE5.8. Thanks, and sorry for the inconvenience.

--Jason Hoerner

[Attachment Removed]