No active graphics context on command list when adding pass to render graph

There’s a crash on D3D12 when trying to obtain context from RHICmdList during pass added to Render Graph.

Pass is added in custom Scene View Extension on PostRenderViewFamily_RenderThread call.

It makes impossible to obtain raw pointer to DirectX12 command list through dynamic RHI.

Hinted by error message “There is no active graphics context on this command list. There may be a missing call to SwitchPipeline().” I checked if the SwitchPipeline is called before the pass. It is called but it does nothing since AcivePipelines is already set to Graphics. Both GraphicsContext and ComputeContext are set to nullptr in the FRHICommandList object during the pass.

Example PostRenderViewFamily_RenderThread call which makes the error:

void FReproSceneViewExtension::PostRenderViewFamily_RenderThread(FRDGBuilder& GraphBuilder, FSceneViewFamily& InViewFamily)
{
	check(IsInRenderingThread());

	auto PassParameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
	PassParameters->RenderTargets[0] = FRenderTargetBinding(InViewFamily.RenderTarget->GetRenderTargetTexture(GraphBuilder), ERenderTargetLoadAction::EClear);
	GraphBuilder.AddPass(RDG_EVENT_NAME("Repro_PostRenderViewFamily"), PassParameters, ERDGPassFlags::Raster | ERDGPassFlags::NeverCull, [this, BackBuffer = InViewFamily.RenderTarget->GetRenderTargetTexture()](FRHICommandList& RHICmdList) {
		auto& Context = RHICmdList.GetContext();

		//used in log so the variable isn't optimized away
		UE_LOG(LogTemp, Warning, TEXT("Context %p"), Context.RHIGetNativeCommandBuffer())
	});
}

Steps to Reproduce

  • Create blank plugin
  • Make new class from Game Instance Subsystem
  • Make new Scene View Extension class
  • Override PostRenderViewFamily_RenderThread in new Scene View Extension
  • Add new pass to GraphBuilder with ERDGPassFlags::Raster | ERDGPassFlags::NeverCull flags
  • Try to call RHICmdList.GetContext() inside new pass body
  • Watch crash with “There is no active graphics context on this command list. There may be a missing call to SwitchPipeline().” error message
  • RHI must be D3D12

Sample plugin provided below. Plugin tested on default Third Person Example

Hi there,

SwitchPipeline doesn’t directly assign the GraphicsContext, instead this is enqueued on the RHICmdList itself as a callback lambda action. You are getting nullptr here because you are trying to access the context before it has been assigned. To fix this you need to enqueue any operations that depend on a valid IRHICommandContext on the RHICmdList itself. This way your operations will be enqueued after the lambda that assigns the graphics context. E.g. your repro project can be fixed like this:

GraphBuilder.AddPass(

RDG_EVENT_NAME(“Repro_PostRenderViewFamily”),

PassParameters,

ERDGPassFlags::Raster | ERDGPassFlags::NeverCull,

[this, BackBuffer = InViewFamily.RenderTarget->GetRenderTargetTexture()](FRHICommandList& RHICmdList)

{

  RHICmdList.EnqueueLambda(TEXT("Test"), \[](FRHICommandList\& InRHICmdList)

  {

     auto\& Context \= InRHICmdList.GetContext();



     *//used in log so the variable isn't optimized away*

UE_LOG(LogTemp, Warning, TEXT(“Context %p”), Context.RHIGetNativeCommandBuffer())

  });

}

);

See the end of FOpenXRHMD::OnBeginRendering_RenderThread(…) for an engine code example of this.

Note that RHIGetNativeCommandBuffer is only implemented on the D3D11 RHI currently, and that other RHIs will return nullptr when calling this method.

To get the native ID3D12GraphicsCommandList*, you probably want to do something like this inside RHICmdList.EnqueueLambda(…)

ID3D12DynamicRHI* D3D12DynamicRHI = GetID3D12DynamicRHI();

ID3D12GraphicsCommandList* D3D12GraphicsCommandList = D3D12DynamicRHI->RHIGetGraphicsCommandList(InRHICmdList, DeviceIndex);

Edit: For this to compile, make sure to add D3D12RHI to the PrivateDependencyModuleNames array in your plugin’s Build.cs. You’ll also need to include the following line to enable native D3D12 types:

AddEngineThirdPartyPrivateStaticDependencies(Target, “DX12”);

Regards,

Lance Chaney