UE5 Compute Shader 入门
Compute Shader
- 添加Shader文件映射
- 创建Shader,继承自FGlobalShader
- 使用RDG创建资源
相关API
FRHICommandListImmediate
- ENQUEUE_RENDER_COMMAND(CommandList)([](FRHICommandListImmediate& RHICmdList){})
- DispatchComputeShader()
- CreateVertexShader()
- CreatePixelShader()
- CreateComputeShader()
- CreateComputeFence()
- CreateGPUFence()
- CreateUniformBuffer()
- CreateAndLockIndexBuffer()
- LockIndexBuffer()
- UnLockIndexBuffer()
- CopyBuffer()
- CopyTexture()
- CreateUnorderedAccessView()
- CreateShaderResourceView()
- CalcTexture2DPlatformSize()
- RHICreateTexture2D()
- CopySharedMips()
- GenerateMips()
- LockTexture2D()
- UnLockTexture2D()
- SuspendRendering()
- ResumeRendering()
- ExecuteCommandList()
FRDGBuilder
- FRDGBuilder GraphBuilder(RHICmdList)
- FComputeShaderUtils::AddPass(GraphBuilder,computeShader)
- GraphBuilder.QueueTextureExtraction(RDGRenderTarget, &PooledRenderTarget)
- GraphBuilder.QueueBufferExtraction(RDGRenderTarget, &PooledBuffer)
- GraphBuilder.Execute()
Buffer
- Source\Developer\ShaderCompilerCommon\Private\HlslLexer.h
- AppendStructuredBuffer
- RWStructuredBuffer
- StructuredBuffer
代码
- TaSimpleColorCS.usf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include "/Engine/Public/Platform.ush"
uint2 TextureSize;
RWTexture2D<float4> OutputTexture;
[numthreads(THREADGROUPSIZE_X, THREADGROUPSIZE_Y, THREADGROUPSIZE_Z)]
void MainCS(uint3 id : SV_DispatchThreadID)
{
float sizeX, sizeY;
OutputTexture.GetDimensions(sizeX, sizeY);
if (id.x < sizeX && id.y < sizeY)
{
float2 uv = float2(id.x / sizeX, id.y / sizeY);
OutputTexture[id.xy] = float4(uv, 0, 1);
}
} - TaCompute.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160#include "TaCompute.h"
#include "Misc/FileHelper.h"
#include "HAL/FileManager.h"
#include "Modules/ModuleManager.h"
#include "IImageWrapper.h"
#include "IImageWrapperModule.h"
#include "Engine/Texture2D.h"
#include "Engine/TextureRenderTarget2D.h"
#include "GlobalShader.h"
#include "RenderGraphUtils.h"
#include "ShaderParameterStruct.h"
#include "RenderGraphBuilder.h"
// RDG 版本
class FComputeShaderRDG : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FComputeShaderRDG)
SHADER_USE_PARAMETER_STRUCT(FComputeShaderRDG, FGlobalShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters,)
SHADER_PARAMETER(FIntPoint, TextureSize)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, OutputTexture) // RDG UAV
END_SHADER_PARAMETER_STRUCT()
static inline FIntVector ThreadGroupSize = FIntVector(16, 16, 1);
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return RHISupportsComputeShaders(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& Environment)
{
Environment.SetDefine(TEXT("THREADGROUPSIZE_X"), ThreadGroupSize.X);
Environment.SetDefine(TEXT("THREADGROUPSIZE_Y"), ThreadGroupSize.Y);
Environment.SetDefine(TEXT("THREADGROUPSIZE_Z"), ThreadGroupSize.Z);
}
static void Execute(FRHICommandListImmediate& RHICmdList, UTextureRenderTarget2D* renderTarget)
{
int32 Width = renderTarget->SizeX;
int32 Height = renderTarget->SizeY;
if (Width < 1 || Height < 1) return;
FRDGBuilder GraphBuilder(RHICmdList);
const TShaderMapRef<FComputeShaderRDG> ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel));
// create resource
const FRDGTextureDesc& RenderTargetDesc = FRDGTextureDesc::Create2D(
FIntPoint(Width, Height),
renderTarget->GetFormat(),
FClearValueBinding::Black,
TexCreate_RenderTargetable |
TexCreate_ShaderResource | TexCreate_UAV);
FRDGTextureRef RDGRenderTarget = GraphBuilder.CreateTexture(RenderTargetDesc, TEXT("RDGRenderTarget"));
FRDGTextureUAVDesc UAVDesc(RDGRenderTarget);
// set params
FParameters* PassParameters = GraphBuilder.AllocParameters<FParameters>();
PassParameters->OutputTexture = GraphBuilder.CreateUAV(UAVDesc);
uint32 GroupSizeX = FMath::DivideAndRoundUp(Width, ThreadGroupSize.X);
uint32 GroupSizeY = FMath::DivideAndRoundUp(Height, ThreadGroupSize.Y);
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("ComputeShaderRDG"),
ERDGPassFlags::Compute,
ComputeShader,
PassParameters,
FIntVector(GroupSizeX, GroupSizeY, 1));
TRefCountPtr<IPooledRenderTarget> PooledRenderTarget;
GraphBuilder.QueueTextureExtraction(RDGRenderTarget, &PooledRenderTarget);
GraphBuilder.Execute();
RHICmdList.CopyTexture(PooledRenderTarget->GetRenderTargetItem().ShaderResourceTexture,
renderTarget->GetRenderTargetResource()->TextureRHI, FRHICopyTextureInfo());
}
};
// 旧版本
class FComputeShaderDemo : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FComputeShaderDemo)
SHADER_USE_PARAMETER_STRUCT(FComputeShaderDemo, FGlobalShader)
BEGIN_SHADER_PARAMETER_STRUCT(FParameters,)
SHADER_PARAMETER(FIntPoint, TextureSize)
SHADER_PARAMETER_UAV(RWTexture2D<float4>, OutputTexture) // Old UAV
END_SHADER_PARAMETER_STRUCT()
static inline FIntVector ThreadGroupSize = FIntVector(16, 16, 1);
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return RHISupportsComputeShaders(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters,
FShaderCompilerEnvironment& Environment)
{
Environment.SetDefine(TEXT("THREADGROUPSIZE_X"), ThreadGroupSize.X);
Environment.SetDefine(TEXT("THREADGROUPSIZE_Y"), ThreadGroupSize.Y);
Environment.SetDefine(TEXT("THREADGROUPSIZE_Z"), ThreadGroupSize.Z);
}
static void Execute(FRHICommandListImmediate& RHICmdList, UTextureRenderTarget2D* renderTarget)
{
check(IsInRenderingThread());
if (!renderTarget) return;
const TShaderMapRef<FComputeShaderDemo> computeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel));
RHICmdList.SetComputeShader(computeShader.GetComputeShader());
int32 Width = renderTarget->SizeX;
int32 Height = renderTarget->SizeY;
if (Width < 1 || Height < 1) return;
// create gpu resource
FRHIResourceCreateInfo TextureInfo(TEXT("TextureInfo"));
const FTexture2DRHIRef GPUOutTexture = RHICreateTexture2D(
Width, Height, renderTarget->GetFormat(), 1, 1,
TexCreate_ShaderResource | ETextureCreateFlags::UAV, TextureInfo);
FUnorderedAccessViewRHIRef GPUOutTextureUAV = RHICreateUnorderedAccessView(GPUOutTexture);
FParameters PassParameters;
PassParameters.OutputTexture = GPUOutTextureUAV;
PassParameters.TextureSize = FIntPoint(Width, Height);
uint32 GroupSizeX = FMath::DivideAndRoundUp(Width, ThreadGroupSize.X);
uint32 GroupSizeY = FMath::DivideAndRoundUp(Height, ThreadGroupSize.Y);
FComputeShaderUtils::Dispatch(RHICmdList, computeShader, PassParameters, FIntVector(GroupSizeX, GroupSizeY, 1));
RHICmdList.CopyTexture(GPUOutTexture,
renderTarget->GetRenderTargetResource()->TextureRHI,
FRHICopyTextureInfo());
GPUOutTextureUAV.SafeRelease();
}
};
IMPLEMENT_GLOBAL_SHADER(FComputeShaderRDG, "/TaTools/Private/TaSimpleColorCS.usf", "MainCS", SF_Compute);
IMPLEMENT_GLOBAL_SHADER(FComputeShaderDemo, "/TaTools/Private/TaSimpleColorCS.usf", "MainCS", SF_Compute);
// 蓝图调用
void UTaCompute::RunComputeShader(UTextureRenderTarget2D* Texture)
{
ENQUEUE_RENDER_COMMAND(CommandList)(
[Texture](FRHICommandListImmediate& RHICmdList)
{
FComputeShaderRDG::Execute(RHICmdList, Texture);
}
);
} - TaCompute.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14#pragma once
#include "CoreMinimal.h"
#include "RHICommandList.h"
#include "Engine/Texture.h"
#include "TaCompute.generated.h"
UCLASS()
class UTaCompute : public UObject
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category="Compute Shader")
static void RunComputeShader(UTextureRenderTarget2D* Texture);
}; - Module.cpp 添加Shader路径映射
1
2
3
4
5void FTaShaderModule::StartupModule()
{
FString PluginShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("TaTools"))->GetBaseDir(), TEXT("Shaders"));
AddShaderSourceDirectoryMapping(TEXT("/TaTools"), PluginShaderDir);
}
参考
- 自定义ComputeShader
- ComputeShader 裁剪
- 虚幻引擎移动端ComputeShader的应用
- Unreal Compute Shader 使用流程笔记
- 在UE4.26里使用ComputeShader生成一张纯色贴图
- https://github.com/blueroseslol/BRPlugins
- https://learn.microsoft.com/zh-cn/windows/win32/direct3dhlsl/sm5-object-appendstructuredbuffer
- https://github.com/AyoubKhammassi/CustomComputeShaders
- Using Compute Shaders in Unreal Engine 4
- COMPUTE SHADER IN UNREAL [TUTORIAL]
UE5 Compute Shader 入门
https://automask.github.io/wild/2022/11/20/lab/S_Unreal_ShaderCompute/