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
    5
    void FTaShaderModule::StartupModule()
    {
    FString PluginShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("TaTools"))->GetBaseDir(), TEXT("Shaders"));
    AddShaderSourceDirectoryMapping(TEXT("/TaTools"), PluginShaderDir);
    }

参考


UE5 Compute Shader 入门
https://automask.github.io/wild/2022/11/20/lab/S_Unreal_ShaderCompute/
作者
Kyle Zhou
发布于
2022年11月20日
许可协议