Error in Computer Shader "Accessing the RHI resource at this time is not allowed" (5.4.2)

Hi,

I have a problem with a compute shader.
I try to run a pass several times for a fluid simulation but I keep getting the following error:

Assertion failed: DebugData->bAllowRHIAccess || GRDGAllowRHIAccess [File:D:\build\++UE5\Sync\Engine\Source\Runtime\RenderCore\Private\RenderGraphValidation.cpp] [Line: 56] 
Accessing the RHI resource of Pressure0 at this time is not allowed. If you hit this check in pass, that is due to this resource not being referenced in the parameters of your pass.

Pressure0 (and Pressure1) being the two FRDGTextureRefs created beforehand. Here is the paragraph in question:
FireSimulation.cpp

If I comment this block out, everything runs fine as far as I can tell. Anyone has an idea, what I’m doing wrong?

Cheers,
Markus

Neither “current velocity state” or “divergence result” are valid parameters.
a) you cannot have spaces in your variable names
b) there is nothing even resembling these variables carrying any type of value.

Are they some sort of temporary descriptive variables for parameters that are planned for implementation?

No, they are just comments to indicate which results are currently stored in the temporary textures that I need in the previous passes.

How can I check if the function works correctly? What can I expect to see?

The system was constantly complaining about parameters not being ready. So I decided to push the allocation out of the loop and do it once.

						FFireShaderPressureCS::FParameters* Params = GraphBuilder.AllocParameters<FFireShaderPressureCS::FParameters>();

						for(int32 I=1; I < Config.NumPressureIterations; ++I)
						{							
							
							Params->VelocityBounds = Velocity.Bounds;							
							Params->obstaclesIn = GraphBuilder.CreateSRV(ObstaclesTexture);
							Params->divergenceIn = GraphBuilder.CreateSRV(Divergence);
							
							Params->pressureIn = GraphBuilder.CreateSRV(Pressure[SourceIndex]);							
							Params->outputFloat = GraphBuilder.CreateUAV(Pressure[DestIndex]);
																					
							GraphBuilder.AddPass(
								RDG_EVENT_NAME("Pressure"),
								Params,
								ERDGPassFlags::AsyncCompute,								
								[&Params, Shader, GroupCount,I](FRHIComputeCommandList& RHICmdList)
								{																													
UE_LOG(LogTemp, Warning, TEXT("pressureIn: %d"), Params->pressureIn);
										FComputeShaderUtils::Dispatch(RHICmdList, Shader, *Params, GroupCount);																			
								});
							
							Swap(SourceIndex, DestIndex);

						}
					}
				}
			}

I’m getting the following readings


Are they within the range of expected parameters for pressure?

that looks normal. the log output is the pointer of the pressure srv. you should display this in hex, not decimal.

btw the loop should be

for(int32 I=1; I <= Config.NumPressureIterations; I++)

to get the 8 iterations.

Needs to start from 0 because it’s comparing less than and not less than even to get full 8 loops

for(int32 I=0; I < Config.NumPressureIterations; I++)
{							
}

I was mostly addressing the crash though

Not really sure if you can even output FRDGTextureSRVRef properly.

Actually not because the first iteration is already done in the prepare pass which fills the Pressure[0] texture.

Did moving the allocation above the loop fix the crash? Does it work as intended?

I’m not sure the paramters are stored somewhere, so wouldn’t that change the params from the earlier passes?

They are assigned on a per loop basis and automatically dispatched in the next line, so I don’t think so. If they were in the loop they would be destroyed anyway because of how scopes work.

As it turns out, the parameters are created on the heap and managed by the GraphBuilder.
So modifying the Params struct after it got sent to the Pass is a no-no. Scope has nothing to do with it.

After a little more experimenting I noticed that if you remove the swap command Swap(SourceIndex, DestIndex); the project works.

Upon re-enabling it I think that the problem is that the pressure textures assigned in

Params->pressureIn = GraphBuilder.CreateSRV(PressureTemp[SourceIndex]);
Params->outputFloat = GraphBuilder.CreateUAV(PressureTemp[DestIndex]);

are marked as used and become unusable inside of the loop as their resources are probably released after assignment or locked as they have been sent in dispatch.

Changed code

for(int32 I=1; I < Config.NumPressureIterations; ++I)
{
				FFireShaderPressureCS::FParameters* Params = GraphBuilder.AllocParameters<FFireShaderPressureCS::FParameters>();

				const FRDGTextureRef PressureTemp[2] =
				{
					GraphBuilder.CreateTexture(CreateTextureDesc(Velocity.Resolution, false), TEXT("Pressure0")),
					GraphBuilder.CreateTexture(CreateTextureDesc(Velocity.Resolution, false), TEXT("Pressure1"))
				};
											
				Params->VelocityBounds = Velocity.Bounds;							
				Params->pressureIn = GraphBuilder.CreateSRV(PressureTemp[SourceIndex]);
				Params->divergenceIn = GraphBuilder.CreateSRV(Divergence);
				Params->obstaclesIn = GraphBuilder.CreateSRV(ObstaclesTexture);
				Params->outputFloat = GraphBuilder.CreateUAV(PressureTemp[DestIndex]);

				GraphBuilder.AddPass(
					RDG_EVENT_NAME("Pressure"),
					Params,
					ERDGPassFlags::AsyncCompute,
					[&Params, Shader, GroupCount, I](FRHIComputeCommandList& RHICmdList)
					{																		
						FComputeShaderUtils::Dispatch(RHICmdList, Shader, *Params, GroupCount);
					}
				);
				
				Swap(SourceIndex, DestIndex);														
}

It stops the RHI error, but it does trigger a single MarkAsConsumed error.

If could be due to persure0 being empty during the first pass

Edit:

As a further optimization the descriptions themselves can be pulled out of the loop, they don’t seem to loose the RHI as I think it might be generated and assigned from within GraphBuilder.CreateTexture()

FRDGTextureDesc p0 = CreateTextureDesc(Velocity.Resolution, false);
FRDGTextureDesc p1 = CreateTextureDesc(Velocity.Resolution, false);

for(int32 I=0; I < Config.NumPressureIterations; I++)
{
				FFireShaderPressureCS::FParameters* Params = GraphBuilder.AllocParameters<FFireShaderPressureCS::FParameters>();

				const FRDGTextureRef PressureTemp[2] =
				{
					GraphBuilder.CreateTexture(p0, TEXT("Pressure0")),
					GraphBuilder.CreateTexture(p1, TEXT("Pressure1"))
				};
/// rest of code 
}

But that doesn’t make any sense anymore. The shader needs the output from the previous pass:

  1. PressureCS Input: Pressure0, Output: Pressure1
  2. PressureCS Input: Pressure1, Output: Pressure0
  3. PressureCS Input: Pressure0, Output: Pressure1

perhaps you need to make it sharable for it to not become invalidated?

TSharedPtr<FRDGTextureRef> SharedPressure = MakeShareable(Pressure);

how async is the compute? i mean you have to wait for the result of one iteration before you do the next. this will not run in parallel. the resources are locked while it does one compute pass. if you would tile the resources you could run the algorithm in parallel per tile. in the end you have to wait for the entire loop to finish, anyway. given it runs at full compute speed it will not be faster either way.

you can just switch to ERDGPassFlags::Compute not that fixes the situation.

But why does it work with the previous passes?
There are quite a lot of output textures that are used as input in the next pass.

I’m not sure that this works as the FRDGTexture is managed by the GraphBuilder and valid throughout it’s lifetime.

Perhaps you should somehow push this to a buffer first and then assigned in the loop

I’ve noticed that the engine has
FRDGBufferSRVRef
FRDGBufferSRV

This might not cause direct access errors.