Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UE5.3 Passing StructuredBuffer to Shader #14

Open
ChrisSchroeder01 opened this issue Dec 18, 2023 · 1 comment
Open

UE5.3 Passing StructuredBuffer to Shader #14

ChrisSchroeder01 opened this issue Dec 18, 2023 · 1 comment

Comments

@ChrisSchroeder01
Copy link

Hello,

I am currently trying to pass a Structured Buffer to the shader. The compute shader should also be able to change the values of the structs in this Structured Buffer over time (multiple executions).

I have a struct:

struct FAgent
{
	FVector2f position;
	float angle;
};

And my Execute RTComputeShader method:

static void ExecuteRTComputeShader(UTextureRenderTarget2D* RT, float deltaTime, FVector2f res, TArray<FAgent> agents)
{
	// Create a dispatch parameters struct and fill it the input array with our args
	FSlimeCSDispatchParams Params(agents.Num(), 1, 1);
	Params.Resolution = static_cast<FVector2f>(res);
	Params.RenderTarget = RT->GameThread_GetRenderTargetResource();
	Params.DeltaTime = deltaTime;
	Params.Agents = &agents;

	FSlimeCSInterface::Dispatch(Params);
}

I got the pass to shader working with the following in the DispatchRenderThread Method:

FRDGBufferRef AgentBuffer = CreateStructuredBuffer(GraphBuilder, TEXT("AgentBuffer"), sizeof(FAgent), Params.Agents->Num(), Params.Agents->GetData(), sizeof(FAgent) * Params.Agents->Num());
PassParameters->Agents = GraphBuilder.CreateUAV(AgentBuffer, PF_R32_FLOAT);

Unfortunately I could not figure out how to create an external buffer, which i assume will fix my issue that the computed data of my compute shader will not be overwritten. I tryied RegisterExternalBuffer but this Function is undefined. FRDGBuilder as a method called RegisterExternalBuffer but this method requires a TRefCountPtr parameter, but I could not figure out how to get such a value.

I am using the following Shader Parameter Definition:
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FAgent>, Agents)
because the pre-generated commented propose SHADER_PARAMETER_UAV(RWStructuredBuffer<FMyCustomStruct>, MyCustomStructs) // On the shader side: RWStructuredBuffer<FMyCustomStruct> MyCustomStructs; again I could not figure out how to get a FRHIUnorderedAccessView * from the TArray.

I came across several posts also trying to achieve what I want:
https://forums.unrealengine.com/t/loading-data-to-from-structured-buffer-compute-shaders/470083
https://forums.unrealengine.com/t/rwbuffer-and-buffer-in-compute-shaders-when-they-binding-with-the-same-resource/508164

There are a few things I noticed, it seems that Epic Games changed things from for the 5.3 Version. For example FRHICommandListImmediate::LockStructuredBuffer does not exist anymore (see: https://docs.unrealengine.com/5.3/en-US/API/Runtime/RHI/FRHICommandListImmediate/LockStructuredBuffer/)

I also looked at the Indirect Instancing examples but they looked pretty complex on the first sight. It would generally be nice to have a single Compute Shader Example with every possible Shader Parameter (the 7 commented ones) to know what to do for each parameter type.

@AskingQuestions
Copy link
Owner

So if I'm understanding you correctly, you want a long-lived buffer on the GPU (one that persists through multiple frames)?

If so, the indirect instancing example has this setup, and the name of the relevant array is RWBaseInstanceBuffer. But it is complicated as you mentioned, so I'll distill it here:

Note

This is crudely adapted from the indirect instancing example and can be simplified a bit

compute.usf

struct MeshItem {
	float3 Position;
	float3 Rotation;
	float3 Scale;
};

// This is our buffer in the shader
RWStructuredBuffer<MeshItem> RWBaseInstanceBuffer;

main.h - under the shader definition

BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FMeshItem>, RWBaseInstanceBuffer)
END_SHADER_PARAMETER_STRUCT()

main.cpp

/** Structure to carry RDG resources. */
struct FVolatileResources
{
  FRDGBufferRef BaseInstanceBuffer;
  FRDGBufferUAVRef BaseInstanceBufferUAV;
  FRDGBufferSRVRef BaseInstanceBufferSRV;
};

main.cpp - when you dispatch

// ...
PassParameters->RWBaseInstanceBuffer = InVolatileResources.BaseInstanceBufferUAV;
// ...
struct ShaderContext {
  // Multi-frame buffers used to store the instance data.
  // This is initialized in InitializeResources()
  mutable TRefCountPtr<FRDGPooledBuffer> BaseInstanceBuffer;
}

main.cpp - when we need to initialize stuff for our shader we call this and pass in a long-lived shader context and volatile resources

/** Initialize the volatile resources used in the render graph. */
void InitializeResources(FRDGBuilder &GraphBuilder, ShaderContext &Ctx, FVolatileResources &OutResources)
{
  if (!Ctx.BaseInstanceBuffer.IsValid())
  {
    // We need to create the instance buffers.
    OutResources.BaseInstanceBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(FMeshItem), MAX_RENDER_ITEMS), TEXT("Mesh.BaseInstanceBuffer"));
    Ctx.BaseInstanceBuffer = GraphBuilder.ConvertToExternalBuffer(OutResources.BaseInstanceBuffer);
  }
  else
  {
    // Buffers already exist, we can use them.
    OutResources.BaseInstanceBuffer = GraphBuilder.RegisterExternalBuffer(Ctx.BaseInstanceBuffer);
  }
  
  OutResources.BaseInstanceBufferUAV = GraphBuilder.CreateUAV(OutResources.BaseInstanceBuffer);
  OutResources.BaseInstanceBufferSRV = GraphBuilder.CreateSRV(OutResources.BaseInstanceBuffer);
}

Hopefully that captures it correctly. Let me know if something's not working or if you need help finding where to put it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants