ue4系列: shader

简单记录一下ue中global shader的注册、编译和使用。ue的shader里面各种宏乱飞,不得不说,宏是各种引擎里面,最恶心的地方了,尤其是uber shader的大一统写法,不过也算是有舍有得。

本文以PostProcessSubsurface.cpp/PostProcessSubsurface.usf的MainIndirectDispatchCS为例。

  1. global shader中宏的声明

MainIndirectDispatchCS中使用的宏都在这里定义,利用ShaderPermutation.h把每一个宏包装为一个单独的类。

1
2
3
4
5
6
7
8
9
class FSubsurfacePassFunction : SHADER_PERMUTATION_ENUM_CLASS("SUBSURFACE_PASS", ESubsurfacePass);
class FDimensionQuality : SHADER_PERMUTATION_ENUM_CLASS("SUBSURFACE_QUALITY", EQuality);
class FBilateralFilterKernelFunctionType : SHADER_PERMUTATION_ENUM_CLASS("BILATERAL_FILTER_KERNEL_FUNCTION_TYPE", EBilateralFilterKernelFunctionType);
class FSubsurfaceType : SHADER_PERMUTATION_ENUM_CLASS("SUBSURFACE_TYPE", ESubsurfaceType);
class FDimensionHalfRes : SHADER_PERMUTATION_BOOL("SUBSURFACE_HALF_RES");
class FRunningInSeparable : SHADER_PERMUTATION_BOOL("SUBSURFACE_FORCE_SEPARABLE");
class FDimensionEnableProfileIDCache : SHADER_PERMUTATION_BOOL("ENABLE_PROFILE_ID_CACHE");
using FPermutationDomain = TShaderPermutationDomain<FSubsurfacePassFunction, FDimensionQuality,
FBilateralFilterKernelFunctionType, FSubsurfaceType, FDimensionHalfRes, FRunningInSeparable, FDimensionEnableProfileIDCache>;

  1. shader permutation
1
IMPLEMENT_GLOBAL_SHADER(FSubsurfaceIndirectDispatchCS, "/Engine/Private/PostProcessSubsurface.usf", "MainIndirectDispatchCS", SF_Compute);

ue里面各种宏,看的眼晕。IMPLEMENT_GLOBAL_SHADER实例化一个静态对象(通过IMPLEMENT_SHADER_TYPE),渲染模块加载(FEngineLoop::LoadPreInitModules)时候就会执行,在FShaderType::FShaderType的构造(shader.cpp)中调用GlobalListLink.LinkHead把当前类型注册到全局shader链表中,并通过CompileGlobalShaderMap、VerifyGlobalShaders(遍历shader permutation)、FGlobalShaderTypeCompiler::BeginCompileShader编译。

  1. 使用

Engine/Source/Runtime/RenderCore/Public/Shader.h L2105~L2122

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
template<typename ShaderType>
class TShaderMapRef : public TShaderRef<ShaderType>
{
public:
TShaderMapRef(const typename ShaderType::ShaderMapType* ShaderIndex)
: TShaderRef<ShaderType>(ShaderIndex->template GetShader<ShaderType>(/* PermutationId = */ 0)) // gcc3 needs the template quantifier so it knows the < is not a less-than
{
static_assert(
TIsSame<typename ShaderType::FPermutationDomain, FShaderPermutationNone>::Value,
"Missing permutation vector argument for shader that have a permutation domain.");
}

TShaderMapRef(
const typename ShaderType::ShaderMapType* ShaderIndex,
const typename ShaderType::FPermutationDomain& PermutationVector)
: TShaderRef<ShaderType>(ShaderIndex->template GetShader<ShaderType>(PermutationVector.ToDimensionValueId())) // gcc3 needs the template quantifier so it knows the < is not a less-than
{ }
};

1
2
3
4
5
6
7
8
9
10
11
12
   SHADER::FPermutationDomain ComputeShaderPermutationVector;
ComputeShaderPermutationVector.Set<SHADER::FSubsurfacePassFunction>(SubsurfacePassFunction);
ComputeShaderPermutationVector.Set<SHADER::FDimensionQuality>(SHADER::GetQuality());
ComputeShaderPermutationVector.Set<SHADER::FBilateralFilterKernelFunctionType>(SHADER::GetBilateralFilterKernelFunctionType());
ComputeShaderPermutationVector.Set<SHADER::FSubsurfaceType>(SubsurfaceType);
ComputeShaderPermutationVector.Set<SHADER::FDimensionHalfRes>(bHalfRes);
ComputeShaderPermutationVector.Set<SHADER::FRunningInSeparable>(bForceRunningInSeparable);
ComputeShaderPermutationVector.Set<SHADER::FDimensionEnableProfileIDCache>(bUseProfileIdCache);
TShaderMapRef<SHADER> ComputeShader(View.ShaderMap, ComputeShaderPermutationVector);

FComputeShaderUtils::AddPass(GraphBuilder, FRDGEventName(PassInfo.Name), ComputeShader, PassParameters, SubsurfaceBufferArgs[SubsurfaceTypeIndex], 0);

TShaderMapRef构造中通过shader permutation id从shadermap中获取对应的shader实例,然后通过RenderGraph添加该pass。