RDG应用
RDG的使用,用于备忘,快速复制
本文包括使用CS、增加ScreenPass,增加Mesh Pass
包含大量代码的Low B文章
所用的引擎版本是4.27
一、CS 工具
1.1 测试
一个在插件中辅助计算的CS案例,功能是图像反色

1.1.0 模块初始化时的路径注册
FString PluginShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("XXComputeTools"))->GetBaseDir(), TEXT("Shaders"));
AddShaderSourceDirectoryMapping(FString(TEXT("/XX")), PluginShaderDir);
1.1.1 CS_Zero.h
#pragma once
#include "ShaderParameterUtils.h"
#include "RHIStaticStates.h"
#include "Shader.h"
#include "GlobalShader.h"
#include "RenderGraphBuilder.h"
#include "RenderGraphUtils.h"
#include "ShaderParameterStruct.h"
#include "UniformBuffer.h"
#include "RHICommandList.h"
#define NUM_THREADS_PER_GROUP_DIMENSION 32
BEGIN_SHADER_PARAMETER_STRUCT(FCS_ZeroParameters, )
SHADER_PARAMETER_TEXTURE(Texture2D<float4>, In_TestTexture)
SHADER_PARAMETER_UAV(RWTexture2D<float4>, Out_TestTexture)
END_SHADER_PARAMETER_STRUCT()
struct NORACOMPUTETOOLS_API FCS_ZeroParameters2
{
UTextureRenderTarget2D* In_TestTexture;
FRHITexture* Out_TestTexture;
};
class NORACOMPUTETOOLS_API FCS_ZeroShader : public FGlobalShader
{
public:
DECLARE_SHADER_TYPE(FCS_ZeroShader, Global);
SHADER_USE_PARAMETER_STRUCT(FCS_ZeroShader, FGlobalShader);
/*
检查是否支持
*/
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return true;
}
/*
编译时调用,填充FShaderCompileJob
*/
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_X"), NUM_THREADS_PER_GROUP_DIMENSION);
OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_Y"), NUM_THREADS_PER_GROUP_DIMENSION);
OutEnvironment.SetDefine(TEXT("THREADGROUPSIZE_Z"), 1);
}
using FParameters = FCS_ZeroParameters;
};
//注册
IMPLEMENT_GLOBAL_SHADER(FCS_ZeroShader, "/Nora/CS_Zero.usf", "CS_Zero", SF_Compute);
class NORACOMPUTETOOLS_API FCS_ZeroClass
{
public:
static void RunComputeShader_RenderThread(FRHICommandListImmediate& RHICmdList, const FCS_ZeroParameters2& DrawParameters);
};
1.1.2 CS_Zero.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "ComputeShaders/CS_Zero.h"
#include "Engine/TextureRenderTarget2D.h"
void FCS_ZeroClass::RunComputeShader_RenderThread(FRHICommandListImmediate& RHICmdList, const FCS_ZeroParameters2& DrawParameters)
{
TShaderMapRef<fcs_zeroshader> ComputeShader(GetGlobalShaderMap(GMaxRHIFeatureLevel));
FIntVector GroupCounts = FIntVector(FMath::DivideAndRoundUp(DrawParameters.In_TestTexture->SizeX, NUM_THREADS_PER_GROUP_DIMENSION), FMath::DivideAndRoundUp(DrawParameters.In_TestTexture->SizeY, NUM_THREADS_PER_GROUP_DIMENSION), 1);
FUnorderedAccessViewRHIRef TextureUAV = RHICreateUnorderedAccessView(DrawParameters.Out_TestTexture);
FCS_ZeroShader::FParameters Parameters;
Parameters.In_TestTexture = DrawParameters.In_TestTexture->TextureReference.TextureReferenceRHI;
Parameters.Out_TestTexture = TextureUAV;
FComputeShaderUtils::Dispatch(RHICmdList, ComputeShader, Parameters, GroupCounts);
}
1.1.3 蓝图节点
TArray<fcolor> UxxBPLibrary::xx_CS_Zero(UTextureRenderTarget2D* In_Texture, UTextureRenderTarget2D* Out_Texture)
{
// 等串流完成
In_Texture->WaitForStreaming();
Out_Texture->WaitForStreaming();
// 获取RT的源
FTextureRenderTargetResource* RenderTargetResource = Out_Texture->GameThread_GetRenderTargetResource();
// 返回的颜色数组
TArray<fcolor> ReturnArray;
ReturnArray.Init(FColor::White, In_Texture->SizeX * In_Texture->SizeX);
// 创建中转写入目标
FRHIResourceCreateInfo CreateInfo;
FTexture2DRHIRef TempTexture = RHICreateTexture2D(In_Texture->SizeX, In_Texture->SizeY, PF_A32B32G32R32F, 1, 1, TexCreate_ShaderResource | TexCreate_UAV, CreateInfo);
// 设置参数
FCS_ZeroParameters2 Parameters;
Parameters.In_TestTexture = In_Texture;
Parameters.Out_TestTexture = TempTexture;
// 设置栅栏
FRenderCommandFence Fence = FRenderCommandFence();
ENQUEUE_RENDER_COMMAND(CaptureCommand)
(
[&](FRHICommandListImmediate& RHICmdList)
{
// 执行RT
FCS_ZeroClass::RunComputeShader_RenderThread(RHICmdList, Parameters);
// 回读到数组
RHICmdList.ReadSurfaceData(TempTexture, FIntRect(0,0, In_Texture->SizeX, In_Texture->SizeY), ReturnArray, FReadSurfaceDataFlags(RCM_UNorm, CubeFace_MAX));
// 拷贝到RT
FRHICopyTextureInfo Info;
Info.Size = FIntVector(In_Texture->SizeX, In_Texture->SizeY, 1);
RHICmdList.CopyTexture(TempTexture, RenderTargetResource->TextureRHI, Info);
}
);
Fence.BeginFence();
Fence.Wait();
// 释放
TempTexture.SafeRelease();
// 立即更新资源
Out_Texture->UpdateResourceImmediate(false);
return ReturnArray;
}
1.1.4 CS
#include "/Engine/Public/Platform.ush"
Texture2D<float4> In_TestTexture;
RWTexture2D<float4> Out_TestTexture;
[numthreads(THREADGROUPSIZE_X, THREADGROUPSIZE_Y, THREADGROUPSIZE_Z)]
void CS_Zero(
uint3 GroupID : SV_GroupID,
uint3 DispatchThreadId : SV_DispatchThreadID,
uint3 GroupThreadId : SV_GroupThreadID
)
{
Out_TestTexture[DispatchThreadId.xy] = 1.0f - float4(In_TestTexture[DispatchThreadId.xy].xyz, 0.0f);
}
1.2 补充内容
cs并行计算优势很大,一些图像处理相关的引擎工具,可以用CS做提高运行效率。
1.2.1 Barrier
可以等这个Group都执行完Part A之后再执行Part B
//PartA
GroupMemoryBarrierWithGroupSync();
//PartB
1.2.2 Atomic
原子操作
InterlockedAdd(OutHistogramBuffer[histogram_index0], hog_index0_value);
1.2.3 展开循环、分支
有时候固定次数循环可以展开来优化,或者分支展开等操作
UNROLL
for( uint i = 0; i < 9; i++ )
{
sum += InHistogramPow2Buffer[index0 + i];
}
二、Screen Pass

增加一个反色后处理
2.1 创建Shader
Engine/Shaders/Private/PostProcessTestZero.usf
#include "Common.ush"
#include "PostProcessCommon.ush"
#include "DeferredShadingCommon.ush"
#include "ScreenPass.ush"
SCREEN_PASS_TEXTURE_VIEWPORT(Input)
Texture2D InputTexture;
SamplerState InputSampler;
void MainVS(
float4 InPosition : ATTRIBUTE0,
float2 InTexCoord : ATTRIBUTE1,
out noperspective float4 OutUVAndScreenPos : TEXCOORD0,
out float4 OutPosition : SV_POSITION)
{
DrawRectangle(InPosition, InTexCoord, OutPosition, OutUVAndScreenPos);
}
void MainPS(noperspective float4 UVAndScreenPos : TEXCOORD0, out float4 OutColor : SV_Target0)
{
float2 UV = UVAndScreenPos.xy;
OutColor = float4(float3(1.0f,1.0f,1.0f) - Texture2DSample(InputTexture, InputSampler, UV).rgb, 1.0f);
}
Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessTestZero.h
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "ScreenPass.h"
struct FTestZeroInputs
{
FScreenPassRenderTarget OverrideOutput;
FScreenPassTexture SceneColor;
};
FScreenPassTexture AddTestZeroPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, FTestZeroInputs TestZeroInputs);
Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessTestZero.cpp
// Copyright Epic Games, Inc. All Rights Reserved.
#include "PostProcess/PostProcessTestZero.h"
#include "PostProcess/PostProcessCombineLUTs.h"
#include "CanvasTypes.h"
#include "RenderTargetTemp.h"
#include "UnrealEngine.h"
BEGIN_SHADER_PARAMETER_STRUCT(FZeroParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Input)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, InputTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, InputSampler)
END_SHADER_PARAMETER_STRUCT()
class FTestZeroVS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FTestZeroVS);
SHADER_USE_PARAMETER_STRUCT_WITH_LEGACY_BASE(FTestZeroVS, FGlobalShader);
using FParameters = FZeroParameters;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::ES3_1);
}
};
IMPLEMENT_GLOBAL_SHADER(FTestZeroVS, "/Engine/Private/PostProcessTestZero.usf", "MainVS", SF_Vertex);
class FTestZeroPS : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FTestZeroPS);
SHADER_USE_PARAMETER_STRUCT(FTestZeroPS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FZeroParameters, TestZero)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::ES3_1);
}
};
IMPLEMENT_GLOBAL_SHADER(FTestZeroPS, "/Engine/Private/PostProcessTestZero.usf", "MainPS", SF_Pixel);
FScreenPassTexture AddTestZeroPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, FTestZeroInputs TestZeroInputs)
{
check(TestZeroInputs.SceneColor.IsValid());
RDG_EVENT_SCOPE(GraphBuilder, "TestZero");
const FSceneViewFamily& ViewFamily = *(View.Family);
const FScreenPassTextureViewport Viewport(TestZeroInputs.SceneColor);
FScreenPassRenderTarget Output = TestZeroInputs.OverrideOutput;
FRDGTextureDesc OutputDesc = TestZeroInputs.SceneColor.Texture->Desc;
OutputDesc.Reset();
OutputDesc.Flags |= TexCreate_RenderTargetable;
if (!Output.IsValid())
{
Output = FScreenPassRenderTarget(GraphBuilder.CreateTexture(OutputDesc, TEXT("TestZero")), Viewport.Rect, View.GetOverwriteLoadAction());
}
// 创建参数
FTestZeroPS::FParameters* PassParameters = GraphBuilder.AllocParameters<ftestzerops::fparameters>();
PassParameters->TestZero.View = View.ViewUniformBuffer;
PassParameters->TestZero.Input = GetScreenPassTextureViewportParameters(Viewport);
PassParameters->TestZero.InputTexture = TestZeroInputs.SceneColor.Texture;
PassParameters->TestZero.InputSampler = TStaticSamplerState<sf_bilinear, am_clamp,="" am_clamp="">::GetRHI();
PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding();
TShaderMapRef<ftestzerovs> VertexShader(View.ShaderMap);
TShaderMapRef<ftestzerops> PixelShader(View.ShaderMap);
AddDrawScreenPass(
GraphBuilder,
RDG_EVENT_NAME("FTestZero %dx%d (PS)", Viewport.Rect.Width(), Viewport.Rect.Height()),
View,
Viewport,
Viewport,
FScreenPassPipelineState(VertexShader, PixelShader),
PassParameters,
[VertexShader, PixelShader, PassParameters](FRHICommandListImmediate& RHICmdList)
{
SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), PassParameters->TestZero);
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
}
);
return MoveTemp(Output);
}
2.2 增加一个Show flag作为开关
Engine/Source/Runtime/Engine/Public/ShowFlagsValues.inl中增加一个定义
...
/** Screen Pass Zero*/
SHOWFLAG_FIXED_IN_SHIPPING(0, TestZero, SFG_Developer, NSLOCTEXT("UnrealEd", "TestZeroSF", "Test Zero"))
...
在Engine/Source/Runtime/Engine/Public/ShowFlags.h的Init函数中,设置默认值
...
SetTestZero(false);
...
2.3 在RDG中增加Pass
Engine\Source\Runtime\Renderer\Private\PostProcess\PostProcessing.cpp 里有AddPostProcessingPasses和AddMobilePostProcessingPasses分别对应桌面和移动管线。需要分别添加。
2.3.1 将Pass添加到EPass和PassNames[]中
顺序要对应
...
enum class EPass : uint32
{
MotionBlur,
Tonemap,
TestZero,// ←
FXAA,
...
};
...
const TCHAR* PassNames[] =
{
TEXT("MotionBlur"),
TEXT("Tonemap"),
TEXT("TestZero"),//←
TEXT("FXAA"),
...
};
2.3.2 判断是否启用
在合适的位置增加是否启用的判断
if (IsPostProcessingEnabled(View))
{
...
PassSequence.SetEnabled(EPass::TestZero, EngineShowFlags.TestZero);
...
}
else
{
...
PassSequence.SetEnabled(EPass::TestZero, false);
...
}
2.3.3 根据EPass的顺序在合适的位置增加Pass
if (PassSequence.IsEnabled(EPass::TestZero))
{
FTestZeroInputs TestZeroInputs;
PassSequence.AcceptOverrideIfLastPass(EPass::TestZero, TestZeroInputs.OverrideOutput);
TestZeroInputs.SceneColor = SceneColor;
SceneColor = AddTestZeroPass(GraphBuilder, View, TestZeroInputs);
}
三、增加 Mesh Pass

3.1 增加Pass的材质参数
增加新Mesh Pass开关和所使用的材质,以及相关渲染状态变更时的处理
Engine/Source/Runtime/Engine/Classes/Components/PrimitiveComponent.h
/**
* Pass 2
*/
UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadOnly, Category = CustomProperties)
uint8 bSecondPass : 1;
/**
* Pass 2 Material
*/
UPROPERTY(EditAnywhere, AdvancedDisplay, BlueprintReadOnly, Category = CustomProperties)
TArray<umaterialinterface*> SecondPassMaterials;
...
UFUNCTION(BlueprintCallable, Category = "Azure")
void SetSecondPass(bool bInSecondPass);
UFUNCTION(BlueprintCallable, Category = "Azure")
void SetSecondPassMaterial(int32 Index, UMaterialInterface* InSecondPassMaterial);
Engine/Source/Runtime/Engine/Private/Components/PrimitiveComponent.cpp
void UPrimitiveComponent::SetSecondPass(bool bInSecondPass)
{
if (bInSecondPass != bSecondPass)
{
bSecondPass = bInSecondPass;
MarkRenderStateDirty();
}
}
void UPrimitiveComponent::SetSecondPassMaterial(int32 Index, UMaterialInterface* InSecondPassMaterial)
{
if (Index > SecondPassMaterials.Num())
{
SecondPassMaterials.AddZeroed(Index - SecondPassMaterials.Num());
}
if (SecondPassMaterials[Index] != InSecondPassMaterial)
{
SecondPassMaterials[Index] = InSecondPassMaterial;
MarkRenderStateDirty();
}
}
3.2 收集新Mesh Pass的材质
在Engine/Source/Runtime/Engine/Private/Components/MeshComponent.cpp中增加新Mesh Pass材质的关联
FMaterialRelevance UMeshComponent::GetMaterialRelevance(ERHIFeatureLevel::Type InFeatureLevel) const
{
// Combine the material relevance for all materials.
FMaterialRelevance Result;
for(int32 ElementIndex = 0;ElementIndex < GetNumMaterials();ElementIndex++)
{
UMaterialInterface const* MaterialInterface = GetMaterial(ElementIndex);
if(!MaterialInterface)
{
MaterialInterface = UMaterial::GetDefaultMaterial(MD_Surface);
}
Result |= MaterialInterface->GetRelevance_Concurrent(InFeatureLevel);
}
if (bSecondPass && SecondPassMaterials.Num() > 0)
{
for (int MaterialIndex = 0; MaterialIndex < SecondPassMaterials.Num(); MaterialIndex++)
{
if (IsValid(SecondPassMaterials[MaterialIndex]))
{
Result |= SecondPassMaterials[MaterialIndex]->GetRelevance_Concurrent(InFeatureLevel);
}
}
}
return Result;
}
Engine/Source/Runtime/Engine/Private/Components/SkinnedMeshComponent.cpp
Engine/Source/Runtime/Engine/Private/Components/StaticMeshComponent.cpp
中的GetUsedMaterials函数中添加新Mesh Pass所使用的的材质
...
if (bSecondPass && SecondPassMaterials.Num())
{
for (int Index = 0; Index < SecondPassMaterials.Num(); Index++)
{
if (IsValid(SecondPassMaterials[Index]))
{
OutMaterials.Add(SecondPassMaterials[Index]);
}
}
}
...
3.3 PrimitiveComponent新增内容同步到PrimitiveSceneProxy中
在Engine/Source/Runtime/Engine/Public/PrimitiveSceneProxy.h中增加
...
inline const bool SecondPass() const { return bSecondPass; }
inline int32 GetSecondPassMaterialNum() { return SecondPassMaterials.Num(); }
inline UMaterialInterface* GetSecondPassMaterial(int32 Index) const { return SecondPassMaterials.Num() > Index ? SecondPassMaterials[Index] : NULL; }
...
uint8 bSecondPass : 1;
TArray< UMaterialInterface*> SecondPassMaterials;
...
在
Engine/Source/Runtime/Engine/Private/PrimitiveSceneProxy.cpp 构造函数FPrimitiveSceneProxy中从PrimitiveComponent中拷贝参数
...
,bSecondPass(InComponent->bSecondPass)
,SecondPassMaterials(InComponent->SecondPassMaterials)
...
3.4 为新增Mesh Pass再收集一次网格
在Engine/Source/Runtime/Engine/Private/SkeletalMesh.cpp中在迭代当前LOD的Sections时,收集两次
void FSkeletalMeshSceneProxy::GetMeshElementsConditionallySelectable(const TArray<const fsceneview*="">& Views, const FSceneViewFamily& ViewFamily, bool bInSelectable, uint32 VisibilityMap, FMeshElementCollector& Collector) const
{
...
{
...
{
...
for (FSkeletalMeshSectionIter Iter(LODIndex, *MeshObject, LODData, LODSection); Iter; ++Iter)
{
...
// +Second
if (SecondPass() && GetSecondPassMaterial(SectionElementInfo.UseMaterialIndex))
{
FSectionElementInfo SecondElementInfo = FSectionElementInfo(SectionElementInfo);
SecondElementInfo.Material = GetSecondPassMaterial(SecondElementInfo.UseMaterialIndex);
GetDynamicElementsSection(Views, ViewFamily, VisibilityMap, LODData, LODIndex, SectionIndex, bSectionSelected, SecondElementInfo, bInSelectable, Collector);
}
}
...
}
...
}
...
}
在Engine/Source/Runtime/Engine/Private/StaticMeshRender.cpp中增加
void FStaticMeshSceneProxy::DrawStaticElements(FStaticPrimitiveDrawInterface* PDI)
{
...
{
...
{
...
{
...
{
...
for (int32 BatchIndex = 0; BatchIndex < NumBatches; BatchIndex++)
{
FMeshBatch BaseMeshBatch;
if (GetMeshElement(LODIndex, BatchIndex, SectionIndex, PrimitiveDPG, bIsMeshElementSelected, true, BaseMeshBatch))
{
...
// +Second
if (SecondPass() && GetSecondPassMaterialNum() > 0)
{
const FStaticMeshLODResources& LOD = RenderData->LODResources[LODIndex];
const FStaticMeshVertexFactories& VFs = RenderData->LODVertexFactories[LODIndex];
const FStaticMeshSection& Section = LOD.Sections[SectionIndex];
int32 UseMaterialIndex = Section.MaterialIndex;
if (IsValid(GetSecondPassMaterial((UseMaterialIndex))))
{
FMeshBatch SecondMeshBatch;
if (GetMeshElement(LODIndex, BatchIndex, SectionIndex, PrimitiveDPG, bIsMeshElementSelected, true, SecondMeshBatch))
{
SecondMeshBatch.CastShadow = false;
SecondMeshBatch.bUseAsOccluder = false;
SecondMeshBatch.MaterialRenderProxy = GetSecondPassMaterial(UseMaterialIndex)->GetRenderProxy();
PDI->DrawMesh(SecondMeshBatch, ScreenSize);
}
}
}
}
}
}
}
}
}
}
</umaterialinterface*></sf_bilinear,></ftestzerops::fparameters></fcs_zeroshader>

浙公网安备 33010602011771号