[toc]

#

学习 UE5 的渲染。

# Shader 加载流

# 读取虚拟着色期目录

在 UE 引擎对一系列的 args 处理完毕后,会有一个阶段进入 Shader 的加载流程

LaunchEngineLoop.cpp

1
2
3
4
#if WITH_ENGINE
// Add the default engine shader dir
AddShaderSourceDirectoryMapping(TEXT("/Engine"), FPlatformProcess::ShaderDir());
#endif
  1. 检查是否在主线程
1
check(IsInGameThread());
  1. 如果使用 CookData 或者不允许 Shader 编译,就结束流程
1
2
3
4
if (FPlatformProperties::RequiresCookedData()  !AllowShaderCompiling())
{
return;
}
  1. 源码版本的 Shader 在 EngineShaders 目录下 (Combind Engine Dir/Shaders/)

1
2
3
4
5
6
7
8
const TCHAR* FGenericPlatformProcess::ShaderDir()
{
if (Generic_ShaderDir.Len() == 0)
{
Generic_ShaderDir = FPaths::Combine(*(FPaths::EngineDir()), TEXT("Shaders"));
}
return *Generic_ShaderDir;
}
  1. 将着色器目录添加到全局 Mapping 中 (虚拟着色器 -> 真实着色器映射)
1
GShaderSourceDirectoryMappings.Add(VirtualShaderDirectory, RealShaderDirectory);
  1. 自动着色器目录 [项目名]IntermediateShaderAutogen , 同样也会被添加到虚拟着色器映射中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#if WITH_EDITOR
{
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
FString ProjectIntermediateDir = FPaths::ProjectIntermediateDir();
bool bCreateIntermediateSuccess = PlatformFile.CreateDirectoryTree(*ProjectIntermediateDir);
if (!bCreateIntermediateSuccess)
{
UE_LOG(LogInit, Fatal, TEXT("Failed to create Intermediate directory '%s'."), *ProjectIntermediateDir);
}

FString AutogenAbsolutePath = FPaths::ConvertRelativePathToFull(ProjectIntermediateDir / TEXT("ShaderAutogen"));
bool bCreateAutogenSuccess = PlatformFile.CreateDirectory(*AutogenAbsolutePath);
if (!bCreateAutogenSuccess)
{
UE_LOG(LogInit, Fatal, TEXT("Failed to create Intermediate/ShaderAutogen/ directory '%s'. Make sure Intermediate exists."), *AutogenAbsolutePath);
}

AddShaderSourceDirectoryMapping(TEXT("/ShaderAutogen"), AutogenAbsolutePath);
}
#endif //WITH_EDITOR
  1. 加载初始化模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
{
SCOPED_BOOT_TIMING("LoadPreInitModules");
LoadPreInitModules();
}

//内部实现

void FEngineLoop::LoadPreInitModules()
{
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("Loading PreInit Modules"), STAT_PreInitModules, STATGROUP_LoadTime);

// GGetMapNameDelegate is initialized here
#if WITH_ENGINE
FModuleManager::Get().LoadModule(TEXT("Engine"));

FModuleManager::Get().LoadModule(TEXT("Renderer"));

FModuleManager::Get().LoadModule(TEXT("AnimGraphRuntime"));

FPlatformApplicationMisc::LoadPreInitModules();

#if !UE_SERVER
if (!IsRunningDedicatedServer() )
{
if (!GUsingNullRHI)
{
// This needs to be loaded before InitializeShaderTypes is called
FModuleManager::Get().LoadModuleChecked<ISlateRHIRendererModule>("SlateRHIRenderer");
}
}
#endif

FModuleManager::Get().LoadModule(TEXT("Landscape"));

// Initialize ShaderCore before loading or compiling any shaders,
// But after Renderer and any other modules which implement shader types.
FModuleManager::Get().LoadModule(TEXT("RenderCore"));

#if WITH_EDITORONLY_DATA
// Load the texture compressor module before any textures load. They may
// compress asynchronously and that can lead to a race condition.
FModuleManager::Get().LoadModule(TEXT("TextureCompressor"));
#endif

if (!FPlatformProperties::RequiresCookedData())
{
FModuleManager::Get().LoadModule(TEXT("Virtualization"));
}
#endif // WITH_ENGINE

#if WITH_EDITOR
// Load audio editor module before engine class CDOs are loaded
FModuleManager::Get().LoadModule(TEXT("AudioEditor"));

#if !(UE_BUILD_SHIPPING UE_BUILD_TEST) // todo: revist
FModuleManager::Get().LoadModule(TEXT("AnimationModifiers"));
#endif // !(UE_BUILD_SHIPPING UE_BUILD_TEST)
#endif // WITH_EDITOR
}


可以发现,在这一部分,加载了大部分需要的模块,最终对于每一个模块返回的是一个指向模块的指针

1
2
3
4
FORCEINLINE T* Get() const
{
return Ptr;
}

# 代理

UE 理有很多代理,是用来掌控生命周期的,类似于 Unity 的 Update 之类的方法,有生命周期自行调用,其他模块进行注册 其中核心大部分代理都放在 FCoreDelegates 中 举几个最常见的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Called at the beginning of a frame
static FSimpleMulticastDelegate OnBeginFrame;

// Called at the moment of sampling the input (currently on the gamethread)
static FSimpleMulticastDelegate OnSamplingInput;

// Called at the end of a frame
static FSimpleMulticastDelegate OnEndFrame;

// Called at the beginning of a frame on the renderthread
static FSimpleMulticastDelegate OnBeginFrameRT;

// Called at the end of a frame on the renderthread
static FSimpleMulticastDelegate OnEndFrameRT;

只需要在合适的地方调用 Delegate.AddStatic(方法) 即可