Here is the header file for my shader which I created using a tutorial on how to create a compute shader in ue5. I have attempted to get the accelleration structure here but it gives an error that update has not been run.
The .h file
#pragma once
#include "CoreMinimal.h"
#include "GenericPlatform/GenericPlatformMisc.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "SceneInterface.h"
#include "RenderGraphUtils.h"
#include "SceneView.h"
#include "PixelShaderUtils.h"
#include "MeshPassProcessor.inl"
#include "StaticMeshResources.h"
#include "DynamicMeshBuilder.h"
#include "RenderGraphResources.h"
#include "GlobalShader.h"
#include "UnifiedBuffer.h"
#include "CanvasTypes.h"
#include "MaterialShader.h"
#include "RHIGPUReadback.h"
#include "ShaderParameterStruct.h"
#include "RenderTargetPool.h"
#include "RHI.h"
#include "Modules/ModuleManager.h"
#include "C:\Program Files\Epic Games\UE_5.7\Engine\Shaders\Shared\RayTracingDefinitions.h"
#include "C:\Program Files\Epic Games\UE_5.7\Engine\Shaders\Shared\RayTracingPayloadType.h"
#include "C:\\Program Files\\Epic Games\\UE_5.7\\Engine\\Source\\Runtime\\Renderer\\Private\\RayTracing\RayTracingScene.h"
#include "C:\Program Files\Epic Games\UE_5.7\Engine\Source\Runtime\Renderer\Private\SceneRendering.h"
#include "C:\Program Files\Epic Games\UE_5.7\Engine\Source\Runtime\Renderer\Private\ScenePrivate.h"
#include "SceneViewExtension.h"
#include "MySimpleComputeShader.generated.h"
class FScene;
class FSceneView;
struct MYSHADERS_API FMySimpleComputeShaderDispatchParams
{
int X;
int Y;
int Z;
FVector3f Origin;
FVector3f Direction;
float Distance;
FRDGBufferSRVRef LayerView;
FSceneInterface* RenderScene;
const FSceneView* SceneView;
FVector3f Output;
FMySimpleComputeShaderDispatchParams(int x, int y, int z)
: X(x)
, Y(y)
, Z(z)
{
}
};
// This is a public interface that we define so outside code can invoke our compute shader.
class MYSHADERS_API FMySimpleComputeShaderInterface : public FSceneViewExtensionBase {
public:
// Executes this shader on the render thread
static void DispatchRenderThread(
FRHICommandListImmediate& RHICmdList,
FMySimpleComputeShaderDispatchParams Params,
TFunction<void(FVector3f OutputVal)> AsyncCallback
);
// Executes this shader on the render thread from the game thread via EnqueueRenderThreadCommand
static void DispatchGameThread(
FMySimpleComputeShaderDispatchParams Params,
TFunction<void(FVector3f OutputVal)> AsyncCallback
)
{
ENQUEUE_RENDER_COMMAND(SceneDrawCompletion)(
[Params, AsyncCallback](FRHICommandListImmediate& RHICmdList)
{
DispatchRenderThread(RHICmdList, Params, AsyncCallback);
});
}
// Dispatches this shader. Can be called from any thread
static void Dispatch(
FMySimpleComputeShaderDispatchParams Params,
TFunction<void(FVector3f OutputVal)> AsyncCallback
)
{
if (IsInRenderingThread()) {
DispatchRenderThread(GetImmediateCommandList_ForRenderCommand(), Params, AsyncCallback);
}
else {
DispatchGameThread(Params, AsyncCallback);
}
}
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMySimpleComputeShaderLibrary_AsyncExecutionCompleted, const FVector3f, Value);
UCLASS() // Change the _API to match your project
class MYSHADERS_API UMySimpleComputeShaderLibrary_AsyncExecution : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
// Execute the actual load
virtual void Activate() override {
// Create a dispatch parameters struct and fill it the input array with our args
FMySimpleComputeShaderDispatchParams Params(1, 1, 1);
Params.Origin = Arg1;
Params.Direction = Arg2;
Params.Distance = Arg3;
// Dispatch the compute shader and wait until it completes
FMySimpleComputeShaderInterface::Dispatch(Params, [this](FVector3f OutputVal) {
this->Completed.Broadcast(OutputVal);
});
}
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", Category = "ComputeShader", WorldContext = "WorldContextObject"))
static UMySimpleComputeShaderLibrary_AsyncExecution* ExecuteBaseComputeShader(UObject* WorldContextObject, FVector3f Arg1, FVector3f Arg2, float Arg3) {
UMySimpleComputeShaderLibrary_AsyncExecution* Action = NewObject<UMySimpleComputeShaderLibrary_AsyncExecution>();
Action->Arg1 = Arg1;
Action->Arg2 = Arg2;
Action->Arg3 = Arg3;
APlayerController* PC = WorldContextObject->GetWorld()->GetFirstPlayerController();
ULocalPlayer* ULP = PC->GetLocalPlayer();
check(IsValid(ULP));
FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(ULP->ViewportClient->Viewport,WorldContextObject->GetWorld()->Scene,ULP->ViewportClient->EngineShowFlags));
FVector ViewLocation;
FRotator ViewRotation;
const FSceneView* SceneView = ULP->CalcSceneView(&ViewFamily, ViewLocation, ViewRotation, ULP->ViewportClient->Viewport);
const FViewInfo& ViewInfo = static_cast<const FViewInfo&>(*SceneView);
FRayTracingScene::FViewHandle ViewHandle = ViewInfo.GetRayTracingSceneViewHandle();
check(ViewHandle.IsValid())
Action->Arg4 = WorldContextObject->GetWorld()->Scene->GetRenderScene()->RayTracingScene.GetLayerView(ERayTracingSceneLayer::Base,ViewHandle);
Action->Arg5 = WorldContextObject->GetWorld()->Scene;
Action->Arg6 = SceneView;
Action->RegisterWithGameInstance(WorldContextObject);
return Action;
}
UPROPERTY(BlueprintAssignable)
FOnMySimpleComputeShaderLibrary_AsyncExecutionCompleted Completed;
FVector3f Arg1;
FVector3f Arg2;
float Arg3;
FRDGBufferSRVRef Arg4;
FSceneInterface* Arg5;
const FSceneView* Arg6;
};
#define NUM_THREADS_ExampleComputeShader_X 1
#define NUM_THREADS_ExampleComputeShader_Y 1
#define NUM_THREADS_ExampleComputeShader_Z 1
The cpp file:
#include "MySimpleComputeShader.h"
DECLARE_STATS_GROUP(TEXT("MySimpleComputeShader"), STATGROUP_MySimpleComputeShader, STATCAT_Advanced);
DECLARE_CYCLE_STAT(TEXT("MySimpleComputeShader Execute"), STAT_MySimpleComputeShader_Execute, STATGROUP_MySimpleComputeShader);
// This class carries our parameter declarations and acts as the bridge between cpp and HLSL.
class MYSHADERS_API FMySimpleComputeShader : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FMySimpleComputeShader);
SHADER_USE_PARAMETER_STRUCT(FMySimpleComputeShader, FGlobalShader);
class FMySimpleComputeShader_Perm_TEST : SHADER_PERMUTATION_INT("TEST", 1);
using FPermutationDomain = TShaderPermutationDomain<
FMySimpleComputeShader_Perm_TEST
>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
/*
* Here's where you define one or more of the input parameters for your shader.
* Some examples:
*/
// SHADER_PARAMETER(uint32, MyUint32) // On the shader side: uint32 MyUint32;
SHADER_PARAMETER(FVector3f, Origin) // On the shader side: float3 MyVector;
SHADER_PARAMETER(FVector3f, Direction)
SHADER_PARAMETER(float, Distance)
SHADER_PARAMETER_SRV(RayTracingAccelerationStructure, TLAS)
// SHADER_PARAMETER_TEXTURE(Texture2D, MyTexture) // On the shader side: Texture2D<float4> MyTexture; (float4 should be whatever you expect each pixel in the texture to be, in this case float4(R,G,B,A) for 4 channels)
// SHADER_PARAMETER_SAMPLER(SamplerState, MyTextureSampler) // On the shader side: SamplerState MySampler; // CPP side: TStaticSamplerState<ESamplerFilter::SF_Bilinear>::GetRHI();
// SHADER_PARAMETER_ARRAY(float, MyFloatArray, [3]) // On the shader side: float MyFloatArray[3];
// SHADER_PARAMETER_UAV(RWTexture2D<FVector4f>, MyTextureUAV) // On the shader side: RWTexture2D<float4> MyTextureUAV;
// SHADER_PARAMETER_UAV(RWStructuredBuffer<FMyCustomStruct>, MyCustomStructs) // On the shader side: RWStructuredBuffer<FMyCustomStruct> MyCustomStructs;
// SHADER_PARAMETER_UAV(RWBuffer<FMyCustomStruct>, MyCustomStructs) // On the shader side: RWBuffer<FMyCustomStruct> MyCustomStructs;
// SHADER_PARAMETER_SRV(StructuredBuffer<FMyCustomStruct>, MyCustomStructs) // On the shader side: StructuredBuffer<FMyCustomStruct> MyCustomStructs;
// SHADER_PARAMETER_SRV(Buffer<FMyCustomStruct>, MyCustomStructs) // On the shader side: Buffer<FMyCustomStruct> MyCustomStructs;
// SHADER_PARAMETER_SRV(Texture2D<FVector4f>, MyReadOnlyTexture) // On the shader side: Texture2D<float4> MyReadOnlyTexture;
// SHADER_PARAMETER_STRUCT_REF(FMyCustomStruct, MyCustomStruct)
//SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<int>, Input)
SHADER_PARAMETER_RDG_BUFFER_UAV(Buffer<FVector3f>, Output)
END_SHADER_PARAMETER_STRUCT()
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
const FPermutationDomain PermutationVector(Parameters.PermutationId);
return true;
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
const FPermutationDomain PermutationVector(Parameters.PermutationId);
/*
* Here you define constants that can be used statically in the shader code.
* Example:
*/
// OutEnvironment.SetDefine(TEXT("MY_CUSTOM_CONST"), TEXT("1"));
/*
* These defines are used in the thread count section of our shader
*/
OutEnvironment.SetDefine(TEXT("THREADS_X"), NUM_THREADS_ExampleComputeShader_X);
OutEnvironment.SetDefine(TEXT("THREADS_Y"), NUM_THREADS_ExampleComputeShader_Y);
OutEnvironment.SetDefine(TEXT("THREADS_Z"), NUM_THREADS_ExampleComputeShader_Z);
// This shader must support typed UAV load and we are testing if it is supported at runtime using RHIIsTypedUAVLoadSupported
OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads);
//FForwardLightingParameters::ModifyCompilationEnvironment(Parameters.Platform, OutEnvironment);
}
private:
};
// This will tell the engine to create the shader and where the shader entry point is.
// ShaderType ShaderPath Shader function name Type
IMPLEMENT_GLOBAL_SHADER(FMySimpleComputeShader, "/MyShadersShaders/MySimpleComputeShader.usf", "MySimpleComputeShader", SF_Compute);
void FMySimpleComputeShaderInterface::DispatchRenderThread(FRHICommandListImmediate& RHICmdList, FMySimpleComputeShaderDispatchParams Params, TFunction<void(FVector3f OutputVal)> AsyncCallback) {
FRDGBuilder GraphBuilder(RHICmdList);
{
SCOPE_CYCLE_COUNTER(STAT_MySimpleComputeShader_Execute);
DECLARE_GPU_STAT(MySimpleComputeShader);
RDG_EVENT_SCOPE(GraphBuilder, "MySimpleComputeShader");
RDG_GPU_STAT_SCOPE(GraphBuilder, MySimpleComputeShader);
typename FMySimpleComputeShader::FPermutationDomain PermutationVector;
// Add any static permutation options here
// PermutationVector.Set<FMySimpleComputeShader::FMyPermutationName>(12345);
TShaderMapRef<FMySimpleComputeShader> ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel), PermutationVector);
bool bIsShaderValid = ComputeShader.IsValid();
if (bIsShaderValid) {
FMySimpleComputeShader::FParameters* PassParameters = GraphBuilder.AllocParameters<FMySimpleComputeShader::FParameters>();
//const void* RawData = (void*)Params.Origin;
//int NumInputs = 2;
//int InputSize = sizeof(FVector3f);
// FRDGBufferRef InputBuffer1 = CreateUploadBuffer(GraphBuilder, TEXT("InputBuffer"), InputSize, 1, Params.Origin, InputSize);
//PassParameters->Input = GraphBuilder.CreateSRV(FRDGBufferSRVDesc(InputBuffer, PF_R32_SINT));
const FSceneView& View = *Params.SceneView;
TRDGUniformBufferRef<FSceneUniformParameters> Buffer = GetSceneUniformBufferRef(GraphBuilder, View);
PassParameters->Origin = Params.Origin;
PassParameters->Direction = Params.Direction;
PassParameters->Distance = Params.Distance;
FRDGBufferRef OutputBuffer = GraphBuilder.CreateBuffer(
FRDGBufferDesc::CreateBufferDesc(sizeof(FVector3f), 1),
TEXT("OutputBuffer"));
PassParameters->Output = GraphBuilder.CreateUAV(FRDGBufferUAVDesc(OutputBuffer, PF_R32_SINT));
auto GroupCount = FComputeShaderUtils::GetGroupCount(FIntVector(Params.X, Params.Y, Params.Z), FComputeShaderUtils::kGolden2DGroupSize);
FRDGBufferSRVRef layerView = Params.LayerView;
GraphBuilder.AddPass(
RDG_EVENT_NAME("ExecuteMySimpleComputeShader"),
PassParameters,
ERDGPassFlags::AsyncCompute,
[&PassParameters,layerView, ComputeShader, GroupCount](FRHIComputeCommandList& RHICmdList)
{
PassParameters->TLAS = layerView->GetRHI();
FComputeShaderUtils::Dispatch(RHICmdList, ComputeShader, *PassParameters, GroupCount);
});
FRHIGPUBufferReadback* GPUBufferReadback = new FRHIGPUBufferReadback(TEXT("ExecuteMySimpleComputeShaderOutput"));
AddEnqueueCopyPass(GraphBuilder, GPUBufferReadback, OutputBuffer, 0u);
auto RunnerFunc = [GPUBufferReadback, AsyncCallback](auto&& RunnerFunc) -> void {
if (GPUBufferReadback->IsReady()) {
FVector3f* Buffer = (FVector3f*)GPUBufferReadback->Lock(1);
FVector3f OutVal = Buffer[0];
GPUBufferReadback->Unlock();
AsyncTask(ENamedThreads::GameThread, [AsyncCallback, OutVal]() {
AsyncCallback(OutVal);
});
delete GPUBufferReadback;
}
else {
AsyncTask(ENamedThreads::ActualRenderingThread, [RunnerFunc]() {
RunnerFunc(RunnerFunc);
});
}
};
AsyncTask(ENamedThreads::ActualRenderingThread, [RunnerFunc]() {
RunnerFunc(RunnerFunc);
});
}
else {
// We silently exit here as we don't want to crash the game if the shader is not found or has an error.
}
}
GraphBuilder.Execute();
}
The Shader.usf, not implemented anything yet of course except a simple vector return which I have checked works
#include "/Engine/Public/Platform.ush"
#include "/Engine/Private/Common.ush"
#include "/Engine/Private/RayTracing/RayTracingCommon.ush"
#include "/Engine/Private/RayTracing/RayTracingHitGroupCommon.ush"
const float3 Origin;
const float3 Direction;
const float3 Distance;
const RaytracingAccelerationStructure TLAS;
//RWBuffer<float3> Output;
[numthreads(THREADS_X, THREADS_Y, THREADS_Z)]
void MySimpleComputeShader(
uint3 DispatchThreadId : SV_DispatchThreadID,
uint GroupIndex : SV_GroupIndex)
{
//Output[0] = Origin;
}