Issues with custom graphics pass code after changing FRHICommandListImmediate to FRHICommandList

Hello,

We have added a few RDG passes in the deferred shading renderer which does some rendering work and eventually renders to the scene color. In this Unreal Fest 2025 talk https://youtu.be/vnbARZHccpQ?t\=1748 you recommend to avoid the use of FRHICommandListImmediate in new code since it makes parallel translation of command lists impossible.

When adopting this approach in our custom pass by converting our GraphBuilder.AddPass lambdas to use FRHICommandList instead of FRHICommandListImmediate which we were using previously, we notice a litany of graphics issues, mostly flickering, as if our custom pass is occasionally culled.

Is this a sign of some underlying issue in our code? What impact does changing this have on the RHI submission pipeline?

Steps to Reproduce
N/A

Hi Narvin,

Based on your info, you may be doing something incorrectly with RDG or D3D12 (I assume you are on D3D12). Have you tried running with r.RDG.Validation=1 and -d3ddebug (if you are on D3D12)? You would then see some output in your logs if you are violating any usage requirements set forth by the RDG or D3D12. Let me know what you end up finding out.

Hi Tim,

After testing with r.RDG.Validation=1 and -d3ddebug we were able to fix a few issues but this hasn’t solved the underlying problem, I can maybe go into more detail about our use-case. My intuition is that our way of using the RDG is invalid.

The idea is that we have added a few textures to the ViewUniformBuffer in order to have access to these textures in most passes without having to change binding code (eg in the BasePass pixel shaders and in the material graph via custom nodes).

// In FViewUniformShaderParameters:
SHADER_PARAMETER_TEXTURE(Texture3D<float4>, CustomTexture)

The initialization of the view uses a black texture here, until our pass is executed (some details were removed):

FRDGTextureRef Texture = GraphBuilder.CreateTexture(...);
FCustomPassCS::FParameters* Parameters = GraphBuilder.AllocParameters<FCustomPassCS::FParameters>();
Parameters->History = GraphBuilder.RegisterExternalTexture(ViewState.CustomTextureHistory, TEXT("CustomTextureHistory"))
Parameters->OutputTexture = GraphBuilder.CreateUAV(Texture);
// Dispatch compute pass...
 
// Store texture in View for current frame access
View.CustomTexture = Texture;
 
// Update history for next frame.
GraphBuilder.QueueTextureExtraction(View.CustomTexture, &ViewState.CustomTextureHistory, ERHIAccess::SRVMask);
 
// Add the texture to the view so that we can use the texture in future passes/in material shaders without any further engine changes.
GraphBuilder.AddPass(
	RDG_EVENT_NAME("UpdateView"),
	ERDGPassFlags::NeverCull,
	[&View](FRHICommandListImmediate& CommandList)
	{
		View.CachedViewUniformShaderParameters->CustomTexture = View.CustomTexture;
		CommandList.UpdateUniformBuffer(View.ViewUniformBuffer, &*View.CachedViewUniformShaderParameters);
	});

View => FViewInfo&

ViewState => View.ViewState

On line 17, when changing the FRHICommandListImmediate to FRHICommandList, we get the flickering I mentioned in my first post.

Do you have any suggestions for better ways to achieve this?

Thanks!

Narvin

Ok, that’s interesting, and I assume you are not seeing anything related to this error in your engine logs. Just for your info, the Immediate command list is the single submission point for a frame, into which all RHICommandLists record their work per frame, and adds extra functionality for syncing up render resources, whereas a regular RHICommandList is meant for recordable batches of GPU work that can exist concurrently. So my question is actually, why are you trying to switch from recording your work from an RHICommandListImmediate to an FRHICommandList?

I must have misunderstood what was presented in the UnrealFest talk, so in that case for a system/graph builder call which is not designed for concurrent gpu batch work, there is no reason to convert it to use FRHICommandList instead of RHICommandListImmediate?

I have to retract my earlier statement about this, as I had the two concepts reversed in my mind. Yes, you do want to use RHICommandList to take advantage of the parallel translation work we added in 5.6 and avoid the immediate command list.

Otherwise, relating to our use of a ERDGPassFlags::NeverCull graph builder pass to update the View Uniform buffer, this seems to me like a misuse of the RDG. Is there a better way to plug RDG resources into the view uniform buffer (which can only store RHITextures directly)?

Using the NeverCull flag is not necessarily an anti-pattern, as we also use it for other passes within the engine (for example, VirtualShadowMapArray::RenderShadowCasterBoundsRHI). If you want to avoid using the NeverCull flag, you need to realize that the RDG will remove a pass from the graph if your pass does not produce an output that is also consumed by a downstream pass. So if you are going to render onto the scene color render target at some point, the RDG should pick up that data dependency and not cull your pass from the graph. However, seeing as you are using an externally registered texture, you might want to review our RDG guide to check if the lifetime of the CustomTextureHistory actually has the correct lifetime: https://dev.epicgames.com/documentation/unreal\-engine/render\-dependency\-graph\-in\-unreal\-engine\#externalresources

Or would you instead recommend us to bind our resources as RDGTextures wherever appropriate and not hook into the ViewUniformBuffer at all?

Yes, that is likely the best option. Making sure your render resources are as self-contained as possible will help you avoid lifetime-tracking issues and strange side effects that can occur when changing these global-view uniform buffers.

The logs indeed don’t contain anything related to this.

I must have misunderstood what was presented in the UnrealFest talk, so in that case for a system/graph builder call which is not designed for concurrent gpu batch work, there is no reason to convert it to use FRHICommandList instead of RHICommandListImmediate?

Otherwise, relating to our use of a ERDGPassFlags::NeverCull graph builder pass to update the View Uniform buffer, this seems to me like a misuse of the RDG. Is there a better way to plug RDG resources into the view uniform buffer (which can only store RHITextures directly)?

Or would you instead recommend us to bind our resources as RDGTextures wherever appropriate and not hook into the ViewUniformBuffer at all? Our goal in using the view was to minimize the changes we have in the engine compared to the latest UE version to facilitate integrations.