UE5.3 运行ComputeShader

ComputeShader是一些管线类操作或GPU数据处理所必须的一个环节,UE操作ComputeShader不同版本各种崩溃,API也是不断修改。本文基于我测试成功的UE5.3、UE5.4进行流程说明。

本文实现参考github库:
https://github.com/Will-Z666/UE5_ComputerShaderSample

该仓库就我写这篇文章时的状态,异步节点做了一半,传入贴图没有做ComputeShader的测试,因此我在代码中注释了一部分。

没有太多基础的童鞋可以先尝试测试跑通下这篇文章:
https://blog.csdn.net/grayrail/article/details/144704622


1.创建插件

1.1 首先新建一个UE5 C++工程以测试,并创建一个蓝图库插件。

1.2 修改CsRunner.cpp,在模块启动时导入Shader目录的usf:

cpp 复制代码
// Copyright Epic Games, Inc. All Rights Reserved.

#include "CsRunner.h"
#include "Interfaces/IPluginManager.h"
#define LOCTEXT_NAMESPACE "FCsRunnerModule"

void FCsRunnerModule::StartupModule()
{
	const FString ShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("CsRunner"))->GetBaseDir(), TEXT("Shaders"));
	AddShaderSourceDirectoryMapping("/Project/Shaders", ShaderDir);
}

void FCsRunnerModule::ShutdownModule()
{
	// This function may be called during shutdown to clean up your module.  For modules that support dynamic reloading,
	// we call this function before unloading the module.
	
}

#undef LOCTEXT_NAMESPACE
	
IMPLEMENT_MODULE(FCsRunnerModule, CsRunner)

1.3 修改build.cs,设置插件依赖库(slate其实不需要):

cpp 复制代码
PrivateDependencyModuleNames.AddRange(
			new string[]
			{
				"CoreUObject",
				"Engine",
				"Slate",
				"SlateCore",
				"Renderer",
				"RenderCore",
				"RHI",
				"Projects"
            }
			);

1.4 修改uplugin文件,设置插件导入阶段为"PostConfigInit":

cpp 复制代码
"Modules": [
		{
			"Name": "CsRunner",
			"Type": "Runtime",
			"LoadingPhase": "PostConfigInit"
		}
	]

1.5 在插件内目录新建Shader文件夹,新建ComputeShader的usf文件:

1.6 编写该usf文件内容以进行测试:

cpp 复制代码
#include "/Engine/Public/Platform.ush"

RWTexture2D<float3> RenderTarget;
Texture2D<float3> InputTexture;

[numthreads(THREADGROUPSIZE_X, THREADGROUPSIZE_Y, THREADGROUPSIZE_Z)]
void MainComputeShader(
                       uint3 DispatchThreadId : SV_DispatchThreadID,
	                   uint GroupIndex : SV_GroupIndex) 
{
    float x = DispatchThreadId.x;
    float y = DispatchThreadId.y;
    float z = 0;
    RenderTarget[DispatchThreadId.xy] = sin(x);
}

2.核心代码编写

新建ComputeShaderDeclaration.h类,编写调用ComputeShader核心代码。

这部分代码会检查游戏是否在渲染线程,并插入渲染线程执行逻辑。

核心部分DispatchRenderThread在cpp文件中。

cpp 复制代码
#pragma once

#include "CoreMinimal.h"
#include "GenericPlatform/GenericPlatformMisc.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "Engine/TextureRenderTarget2D.h"

struct FCSParameters
{
	int X;
	int Y;
	int Z;


	UTexture2D* InputTexture;

	FRenderTarget* RenderTarget;

	FCSParameters(int x, int y, int z)
		: X(x)
		, Y(y)
		, Z(z)
	{
	}
};

class FComputeShaderInterface
{
public:
	static void DispatchRenderThread(
		FRHICommandListImmediate& RHICmdList,
		FCSParameters Params
	);

	static void DispatchGameThread(
		FCSParameters Params
	)
	{
		ENQUEUE_RENDER_COMMAND(SceneDrawCompletion)(
			[Params](FRHICommandListImmediate& RHICmdList)
			{
				DispatchRenderThread(RHICmdList, Params);
			});
	}

	static void Dispatch(
		FCSParameters Params
	)
	{
		if (IsInRenderingThread()) {
			DispatchRenderThread(GetImmediateCommandList_ForRenderCommand(), Params);
		}
		else {
			DispatchGameThread(Params);
		}
	}
};

新建ComputeShaderDeclaration.cpp类:

cpp 复制代码
#include "ComputeShaderDeclaration.h"

#include "GlobalShader.h"
#include "MaterialShader.h"
#include "ShaderParameterStruct.h"
#include "RenderGraphResources.h"
#include "RenderGraphUtils.h"
#include "RenderGraphBuilder.h"
#include "RenderTargetPool.h"

#define NUM_THREADS_PER_GROUP_DIMENSION_X 32
#define NUM_THREADS_PER_GROUP_DIMENSION_Y 32
#define NUM_THREADS_PER_GROUP_DIMENSION_Z 1

DECLARE_STATS_GROUP(TEXT("ComputeShader"), STATGROUP_ComputeShader, STATCAT_Advanced);
DECLARE_CYCLE_STAT(TEXT("ComputeShader Execute"), STAT_ComputeShader_Execute, STATGROUP_ComputeShader);

class FComputeShader : public FGlobalShader
{
public:
	//Declare this class as a global shader
	DECLARE_GLOBAL_SHADER(FComputeShader);
	//Tells the engine that this shader uses a structure for its parameters
	SHADER_USE_PARAMETER_STRUCT(FComputeShader, FGlobalShader);
	/// <summary>
	/// DECLARATION OF THE PARAMETER STRUCTURE
	/// The parameters must match the parameters in the HLSL code
	/// For each parameter, provide the C++ type, and the name (Same name used in HLSL code)
	/// </summary>
	BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
		SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RenderTarget)
		SHADER_PARAMETER_RDG_TEXTURE(Texture2D, InputTexture)
		END_SHADER_PARAMETER_STRUCT()


public:
	//Called by the engine to determine which permutations to compile for this shader
	static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
	{


		return true;
	}
	//Modifies the compilations environment of the shader
	static inline void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
	{
		FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);

		//We're using it here to add some preprocessor defines. That way we don't have to change both C++ and HLSL code 
		// when we change the value for NUM_THREADS_PER_GROUP_DIMENSION

		OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_X"), NUM_THREADS_PER_GROUP_DIMENSION_X);
		OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_Y"), NUM_THREADS_PER_GROUP_DIMENSION_Y);
		OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_Z"), NUM_THREADS_PER_GROUP_DIMENSION_Z);
	}
};

IMPLEMENT_GLOBAL_SHADER(FComputeShader, "/Project/Shaders/Shader.usf", "MainComputeShader", SF_Compute);


void FComputeShaderInterface::DispatchRenderThread(FRHICommandListImmediate& RHICmdList, FCSParameters Params) {
	FRDGBuilder GraphBuilder(RHICmdList);

	{
		SCOPE_CYCLE_COUNTER(STAT_ComputeShader_Execute);
		DECLARE_GPU_STAT(ComputeShader)
		RDG_EVENT_SCOPE(GraphBuilder, "ComputeShader");
		RDG_GPU_STAT_SCOPE(GraphBuilder, ComputeShader);

		typename FComputeShader::FPermutationDomain PermutationVector;

		TShaderMapRef<FComputeShader> ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel), PermutationVector);

		bool bIsShaderValid = ComputeShader.IsValid();

		if (bIsShaderValid) {
			FComputeShader::FParameters* PassParameters = GraphBuilder.AllocParameters<FComputeShader::FParameters>();


			FRDGTextureDesc Desc(FRDGTextureDesc::Create2D(Params.RenderTarget->GetSizeXY(), PF_B8G8R8A8, FClearValueBinding::White, TexCreate_RenderTargetable | TexCreate_ShaderResource | TexCreate_UAV));
			FRDGTextureRef TmpTexture = GraphBuilder.CreateTexture(Desc, TEXT("ComputeShader_TempTexture"));
			FRDGTextureRef TargetTexture = RegisterExternalTexture(GraphBuilder, Params.RenderTarget->GetRenderTargetTexture(), TEXT("ComputeShader_RT"));
			PassParameters->RenderTarget = GraphBuilder.CreateUAV(TmpTexture);
			//传入贴图这里我注释了
			//FRHITexture* InputTexture = Params.InputTexture->GetResource()->GetTextureRHI();
			//FRDGTextureRef RDGSourceTexture = RegisterExternalTexture(GraphBuilder, InputTexture, TEXT("ComputeShader_InputTexture"));
			//PassParameters->InputTexture = RDGSourceTexture;

			auto GroupCount = FComputeShaderUtils::GetGroupCount(FIntVector(Params.X, Params.Y, Params.Z), FComputeShaderUtils::kGolden2DGroupSize);
			GraphBuilder.AddPass(
				RDG_EVENT_NAME("ExecuteExampleComputeShader"),
				PassParameters,
				ERDGPassFlags::AsyncCompute,
				[&PassParameters, ComputeShader, GroupCount](FRHIComputeCommandList& RHICmdList)
				{
					FComputeShaderUtils::Dispatch(RHICmdList, ComputeShader, *PassParameters, GroupCount);
				});

			if (TargetTexture->Desc.Format == PF_B8G8R8A8) {
				AddCopyTexturePass(GraphBuilder, TmpTexture, TargetTexture, FRHICopyTextureInfo());
			}
		}
	}

	GraphBuilder.Execute();
}

现在C++部分基本已经完成,下面开始讲解蓝图配置部分。

3.蓝图配置

3.1 右键新建RenderTarget,配置好格式参数:

3.2 新建Material,拖入RenedrTarget并设置材质参数:

3.3 将该材质赋予场景中的测试模型。

3.4 新建Actor蓝图,增加如下节点,RenderTarget外部传入即可:

最后在场景中放入该Actor进行测试。

相关推荐
2301_796512526 小时前
Rust编程学习 - 为什么说Cow 代表的是Copy-On-Write, 即“写时复制技术”,它是一种高效的 资源管理手段
java·学习·rust
故里21306 小时前
学习前端记录(二)21-40
学习
ThreeYear_s7 小时前
电力电子技术学习路径与FPGA/DSP技术结合方向(gemini生成)
学习·fpga开发
好奇龙猫7 小时前
【生活相关-日语-日本-入国&出国-海关&市役所(4)-办理手续】
学习·生活
sendnews7 小时前
红松小课如何成为激活老年人生活的新引擎?从兴趣学习到价值重塑!
学习·生活
The_Second_Coming8 小时前
ELK 学习笔记
笔记·学习·elk
wdfk_prog8 小时前
[Linux]学习笔记系列 -- [kernel][time]timekeeping
linux·笔记·学习
2301_796512528 小时前
Rust编程学习 - 如何理解Rust 语言提供了所有权、默认move 语义、借用、生命周期、内部可变性
java·学习·rust
charlie1145141918 小时前
从零开始理解 CSS:让网页“活”起来的语言2
前端·css·笔记·学习·选择器·样式表·原生
im_AMBER8 小时前
Leetcode 46
c语言·c++·笔记·学习·算法·leetcode