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

相关推荐
知识分享小能手1 天前
React学习教程,从入门到精通, React 属性(Props)语法知识点与案例详解(14)
前端·javascript·vue.js·学习·react.js·vue·react
茯苓gao1 天前
STM32G4 速度环开环,电流环闭环 IF模式建模
笔记·stm32·单片机·嵌入式硬件·学习
是誰萆微了承諾1 天前
【golang学习笔记 gin 】1.2 redis 的使用
笔记·学习·golang
DKPT1 天前
Java内存区域与内存溢出
java·开发语言·jvm·笔记·学习
aaaweiaaaaaa1 天前
HTML和CSS学习
前端·css·学习·html
ONLYOFFICE1 天前
【技术教程】如何将ONLYOFFICE文档集成到使用Spring Boot框架编写的Java Web应用程序中
java·spring boot·编辑器
看海天一色听风起雨落1 天前
Python学习之装饰器
开发语言·python·学习
speop1 天前
llm的一点学习笔记
笔记·学习
非凡ghost1 天前
FxSound:提升音频体验,让音乐更动听
前端·学习·音视频·生活·软件需求
ue星空1 天前
月2期学习笔记
学习·游戏·ue5