Writing Data to RDG Structured Buffer / General RDG Questions

I’m glad other people are interested in this too!
Hopefully this thread will be useful to people attempting to implement shaders with RDG in the future.

As for myself, I’ve hit a bit of a snag: it seems that whenever I attempt to run a pass with an FRDGBufferUAVRef parameter I’m able to get the underlying RHI resource for the UAV, but not the buffer the UAV references. Though after some debugging I’ve found that the compute pass completely fails when I use a RDG buffer, in addition to giving the error “Seams shader [shader]'s parameter structure has changed without recompilation of the shader”.

This is odd, especially as the same shader executes fine when using the RHI types, and that the only shader preprocessor definition is a thread group count change.

Here’s the simple shader I’m working with (it takes four uniformly distributed random samples in the range of [0, 1] and produces four Gaussian distributed random samples using the Box-Muller method):



// BoxMuller.usf

RWStructuredBuffer<float4> NoiseBuffer;
uint2 NoiseBufferSize;

static const float Pi = 3.14159265358979323846f;

uint CalculateIndex(uint2 id)
{
 return (id.y * NoiseBufferSize.x) + id.x;
}


[numthreads(THREADGROUPSIZE_X, THREADGROUPSIZE_Y, THREADGROUPSIZE_Z)]
void main( uint3 id : SV_DispatchThreadID )
{
 uint Index = CalculateIndex(id.xy);
 float4 InputNoise = NoiseBuffer[Index];

 float a0 = sqrt(-2 * log(InputNoise.x));
 float t0 = 2 * Pi * InputNoise.y;

 float a1 = sqrt(-2 * log(InputNoise.z));
 float t1 = 2 * Pi * InputNoise.w;

 NoiseBuffer[Index].x = a0 * cos(t0);
 NoiseBuffer[Index].y = a0 * sin(t0);
 NoiseBuffer[Index].z = a1 * cos(t1);
 NoiseBuffer[Index].w = a1 * sin(t1);
}


Here’s the parameter struct definition:



 DECLARE_GLOBAL_SHADER(FBoxMullerShader)
 SHADER_USE_PARAMETER_STRUCT(FBoxMullerShader, FGlobalShader)

 BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
  SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<float4>, NoiseBuffer)
  SHADER_PARAMETER(FIntPoint, NoiseBufferSize)
 END_SHADER_PARAMETER_STRUCT()


And lastly the code for executing a compute pass



unsigned int NumElements = BufferSize.X * BufferSize.Y;

 // Allocate a large array for noise data
 TArray<FVector4> NoiseData;
 NoiseData.Init(FVector4(), NumElements);

 // Generate some random uniform noise
 for (unsigned int i = 0; i < NumElements; i++)
 {
  NoiseData* = FVector4(FMath::FRandRange(0, 1), FMath::FRandRange(0, 1), FMath::FRandRange(0, 1), FMath::FRandRange(0, 1));
 }

 FIntVector BoxMullerGroupCount = FComputeShaderUtils::GetGroupCount(BufferSize, FBoxMullerShader::ThreadsPerGroupDimension);
 FIntVector InitialComponentsGroupCount = FComputeShaderUtils::GetGroupCount(BufferSize, FInitialComponentsShader::ThreadsPerGroupDimension);

 ENQUEUE_RENDER_COMMAND(SceneDrawCompletion)
 ([this, NumElements, BoxMullerGroupCount, InitialComponentsGroupCount, NoiseData]
 (FRHICommandListImmediate& CommandListImmediate)
 {
  FRDGBuilder GraphBuilder(CommandListImmediate);

  FRDGBufferDesc NoiseBufferDesc = FRDGBufferDesc::CreateStructuredDesc(sizeof(FVector4), NumElements);
  FRDGBufferRef NoiseBuffer = GraphBuilder.CreateBuffer(NoiseBufferDesc, TEXT("NoiseBuffer"));
  FRDGBufferUAVRef NoiseBufferUAV = GraphBuilder.CreateUAV(NoiseBuffer);

  FBoxMullerShader::FParameters* BoxMullerParameters = GraphBuilder.AllocParameters<FBoxMullerShader::FParameters>();
  BoxMullerParameters->NoiseBuffer = NoiseBufferUAV;
  BoxMullerParameters->NoiseBufferSize = BufferSize;

  TShaderMapRef<FBoxMullerShader> BoxMullerShader(GetGlobalShaderMap(GMaxRHIFeatureLevel));

  GraphBuilder.AddPass(RDG_EVENT_NAME("Gaussian Noise Generation"), BoxMullerParameters, ERDGPassFlags::Compute,
  &](FRHICommandList& CommandList)
  {
   /* FStructuredBufferRHIRef NoiseBufferRHI = static_cast<FRHIStructuredBuffer*>(BoxMullerParameters->NoiseBuffer->GetParent()->GetRHI());

   FVector4* NoiseBufferData = static_cast<FVector4*>(RHILockStructuredBuffer(NoiseBufferRHI, 0, sizeof(FVector4), EResourceLockMode::RLM_WriteOnly));
   FMemory::Memcpy(NoiseBufferData, NoiseData.GetData(), NumElements);
   RHIUnlockStructuredBuffer(NoiseBufferRHI); */

   FComputeShaderUtils::Dispatch(CommandList, *BoxMullerShader, *BoxMullerParameters, BoxMullerGroupCount);
  });

  GraphBuilder.Execute();
 });


If anyone has any insight as to what might be causing the errors with the RDG buffer while the RHI buffer works fine, I’d appreciate it!