RWBuffer and Buffer in compute shaders when they binding with the same resource

here are some code-snippets to define a Compute Shader which writes into a RW-Buffer

To define a Compute Shader like in your .h:

class FWriteBufferCS : public FGlobalShader
{
	DECLARE_GLOBAL_SHADER( FWriteBufferCS );
	SHADER_USE_PARAMETER_STRUCT( FWriteBufferCS , FGlobalShader );

	BEGIN_SHADER_PARAMETER_STRUCT( FParameters, )
		SHADER_PARAMETER( int, BufferSize )
		SHADER_PARAMETER_UAV( RWStructuredBuffer<float>, BufferTarget )
	END_SHADER_PARAMETER_STRUCT()

public:
	static bool ShouldCompilePermutation( const FGlobalShaderPermutationParameters& Parameters ) {
		return IsFeatureLevelSupported( Parameters.Platform, ERHIFeatureLevel::SM5 );
	}

	static void ModifyCompilationEnvironment( const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment ) {
		OutEnvironment.SetDefine( TEXT( "SOMECONSTANT" ), 123 );
	}
};

dont forget to “implement” the shader file:

IMPLEMENT_SHADER_TYPE( , FWriteBufferCS , TEXT( "/Plugin/YourPlugin/WriteBuffer.usf" ), TEXT( "MainCS " ), SF_Compute )

Setting the Shader Parameters and Dispatch the Compute Shader in .cpp (in the RHI Thread):

FIntVector ThreadGroupAmount;
ThreadGroupAmount.X = FMath::DivideAndRoundUp( BufferSize .X, 64 /*ThreadGroupSize*/ );
ThreadGroupAmount.Y = 1;
ThreadGroupAmount.Z = 1;

FWriteBufferCS::FParameters ShaderParameters;
ShaderParameters.BufferTarget = BufferTarget;
ShaderParameters.BufferSize = BufferSize;

TShaderMapRef< FWriteBufferCS> ComputeShader( GetGlobalShaderMap( GMaxRHIFeatureLevel ) );
FComputeShaderUtils::Dispatch( RHICmdList, ComputeShader, ShaderParameters, ThreadGroupAmount );

in the .usf

RWStructuredBuffer<float> BufferTarget;

[ numthreads( 64, 1, 1 ) ]
void MainCS( uint ThreadId : SV_DispatchThreadID )
{
	if( ThreadId >= BufferSize )
		return;

	BufferTarget[ ThreadId ] = sin( (float)ThreadId * 0.123f );
}

to create a Buffer/UAV you can do kinda this (i think this also works on the Game-Thread) (setting the Resource-Array is optional, since you write the buffer on the gpu anyway, otherwise a MemCpy is best):

FRHIResourceCreateInfo CreateInfo( TEXT( "WriteBuffer" ) );

TResourceArray< float > ResourceArray;
ResourceArray.SetNumZeroed( BufferSize );
CreateInfo.ResourceArray = &ResourceArray;

FStructuredBufferRHIRef BufferSource = RHICreateStructuredBuffer(
	sizeof( float ),
	sizeof( float ) * BufferSize ,
	BUF_UnorderedAccess | BUF_ShaderResource,
	CreateInfo
);

FUnorderedAccessViewRHIRef BufferTarget = RHICreateUnorderedAccessView( BufferSource , false, false );

when you just read data in the gpu-buffer, you can use an SHADER_PARAMETER_SRV( StructuredBuffer<float>, BufferSource ), skipping the ‘RW’ prefixes and just pass the BufferSource into the shader parameters.

Anyway you may call some TransitionResource, WaitForDispatch, or FlushRenderingCommands.

hope that helps, sorry for any typos xD

3 Likes