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进行测试。

相关推荐
香蕉可乐荷包蛋3 分钟前
Python学习之路(十三)-常用函数的使用,及优化
开发语言·python·学习
许白掰28 分钟前
Linux入门篇学习——借助 U 盘或 TF 卡拷贝程序到开发板上
linux·学习·借助 u 盘拷贝程序到开发板上·借助 tf卡拷贝程序到开发板上
爱吃烤鸡翅的酸菜鱼1 小时前
IDEA高效开发:Database Navigator插件安装与核心使用指南
java·开发语言·数据库·编辑器·intellij-idea·database
小梦白2 小时前
RPG59.玩家拾取物品三:可拾取物品的提示UI
ui·ue5
小妖66611 小时前
vscode 源码编译
ide·vscode·编辑器
iFulling12 小时前
【计算机网络】第四章:网络层(上)
学习·计算机网络
香蕉可乐荷包蛋12 小时前
AI算法之图像识别与分类
人工智能·学习·算法
xiaoli232712 小时前
课题学习笔记1——文本问答与信息抽取关键技术研究论文阅读(用于无结构化文本问答的文本生成技术)
笔记·学习
人生游戏牛马NPC1号13 小时前
学习 Flutter (四):玩安卓项目实战 - 中
android·学习·flutter
focksorCr14 小时前
在VsCode上使用开发容器devcontainer
ide·vscode·编辑器