BlaXuan 发表于 2022-12-28 19:31

unreal 5.0中可用的 PSO 类型以及生成 PSO 缓存的详细过程 ...

早期的图形 API,例如Direct3D 11,需要在发出绘制调用之前进行数十次单独调用以动态配置 GPU 参数。最近的图形 API,例如Direct3D 12 (D3D12)、Vulkan和Metal,支持使用预配置的 GPU 状态信息包(称为管道状态对象 (PSO))来更快地更改 GPU 状态。
虽然这大大提高了渲染效率,但按需生成新的 PSO 可能需要 100 或更多毫秒,因为应用程序必须配置每个可能的参数。这使得有必要在需要 PSO 使其高效之前很久就生成 PSO。
在虚幻引擎 (UE)等高度可编程的实时渲染环境中,任何具有大量内容的应用程序都有太多可以更改的 GPU 状态参数,使得提前手动配置 PSO 变得切实可行。为了解决这个问题,UE 可以在运行时从应用程序构建中收集有关 GPU 状态的数据,然后使用这些缓存数据在使用它们之前远远提前生成新的 PSO。这将可能的 GPU 状态缩小到仅在应用程序中使用的状态。从运行应用程序收集的 PSO 描述称为PSO 缓存。
在 Unreal 中收集 PSO 的步骤是:

[*]玩游戏。
[*]记录实际绘制的内容。
[*]在构建中包含此信息。
之后,在后续游戏中,游戏可以在渲染代码需要它们之前创建必要的 GPU 状态。
本文档描述了 UE 中可用的 PSO 类型以及生成 PSO 缓存的详细过程。
术语和支持的 PSO 类型

本文档普遍使用术语管道状态对象 (PSO) 来指代 GPU 状态,该名称在 D3D12 API 中使用。其他 API 使用的名称略有不同。比如 Vulkan 使用pipeline,Metal 使用pipeline state。但是,从概念上讲,它们都是相似的。
术语PSO 缓存是指包含在构建中的具有 PSO 描述的文件,因此游戏可以及早创建这些状态。换句话说,PSO 缓存是早期创建的 PSO 列表。
虚幻引擎支持两种类型的 PSO:

[*]图形 PSO,表示应用程序图形管道的状态,由许多可配置变量组成。
[*]计算 PSO,通常采用计算着色器的形式。
除了上述PSO类型外,还存在光线追踪PSO ,但UE 5.0不支持对光线追踪数据进行PSO缓存。
本文档中的大部分信息都与图形 PSO 集合有关。计算 PSO 以不同方式包含在构建中(着色器库中的所有计算着色器将在烘焙期间自动为它们创建 PSO 条目)。
记录缓存和稳定缓存

PSO 中最重要的数据是着色器信息。但是,随着开发人员调整材质,Unreal 中的着色器可以在多个构建之间更改。为了避免在长时间运行应用程序后丢弃整个 PSO 缓存,PSO 缓存文件分为两种:

[*].upipelinecache文件,或记录的 PSO 缓存。这些是在运行应用程序构建时记录的。

[*]记录缓存中的着色器由其字节码的 SHA 哈希值标识。



[*].spc文件,或稳定的 PSO 缓存。这些是在烘焙贴图文件时生成的,并且包含着色器信息,当开发人员更改项目中的贴图或着色器时,这些信息会发生可预见的变化。

[*]这些由稳定的高级描述标识,这些描述预计在多个构建之间保持相同,例如材质名称、顶点工厂名称或着色器类型。此描述称为稳定密钥,并由.shk文件(以前.scl.csv)表示。


这确保记录的数据相对稳健,不会频繁更改。如果您的应用程序发生非常大规模的更改,您可能仍需要重新记录 PSO 缓存,但一旦您的应用程序的整体内容完成,您就可以不理会它们。
不同平台和图形 API 上的 PSO

本文档中描述的缓存包含高级 PSO 描述,在引擎的代码中映射到 FGraphicsPipelineStateInitializer。然而,PSO 数据并不通用。Unreal Engine 中的每个渲染硬件接口 (RHI)都具有不同的属性,并且可能执行不同的渲染路径。
这导致 PSO 缓存的内容因平台和渲染级别而异。这些 PSO 缓存之间的信息不可互换。例如,使用 D3D12 RHI 运行的游戏收集的缓存不适用于在 Vulkan 上运行的同一游戏。
如果您在一个可以使用多个图形 API 的平台上发布,并且您可以让您的应用程序在它们之间进行选择,那么您将需要在构建中包含多个缓存文件(每个 API 一个)。例如,在 Android 上,OpenGL ES 在撰写本文时仍然是一个相关的 API。如果您在 Android 上同时使用 GLES 和 Vulkan 发布应用程序,则需要收集并包含两个单独的缓存文件,每个 RHI 一个。
GLES 没有 PSO 的概念。相反,它使用称为程序对象的类似概念。
具有固定硬件的平台通常既不需要也不会受益于这种更高级别的 PSO 缓存——它们要么有自己的解决方案,要么能够完全避免运行时性能损失。如果针对此类平台进行开发,请参阅特定平台的文档。
此缓存也不支持 D3D11 等旧版 API。
收款流程

本节假定您从头开始为 PSO 缓存收集数据,构建中不包含任何预先记录的缓存。PSO 缓存的收集过程是迭代的,这意味着您可以在不丢失以前数据的情况下逐步添加,而不是从零开始。但是,如果缓存文件是旧的,那么重新生成 PSO 缓存通常是个好主意,因为它们的内容在大量代码或内容更改后可能不再相关,因为它们最初是收集的。


以下部分包含收集 PSO 缓存并在您的项目中实施它们所需的步骤。
1. 安装和所需设置

以下步骤将设置您的项目以记录 PSO 缓存。

[*]打开项目的DefaultEngine.ini或其(Platform)Engine.ini。
该(Platform)Engine.ini文件通常位于 (Project)/Config/(Platform) 目录中。使用此目录可以防止其他可能不需要 PSO 缓存的平台获取设置。
[*]在文件中设置以下值Engine.ini:
NeedsShaderStableKeys=true
[*]在您的DefaultGame.ini文件中,设置以下值:
bShareMaterialShaderCode=True bSharedMaterialNativeLibraries=True
[*]如果您是从头开始,请确保(Project)/Build/(Platform)/PipelineCache. 这是 cooker 找到记录的缓存文件的位置。如果这是您第一次收集 PSO,则此文件夹可能根本不存在。
[*]确保 CVarr.ShaderPipelineCache.Enabled设置为 1。
2. 收集记录的 PSO 缓存

要记录 PSO 缓存,请执行以下步骤:

[*]使用 -logPSO 命令行开关运行打包的应用程序。
[*]在应用程序中执行尽可能多的路径。例如,播放应用程序的所有关卡并更改图形设置。
[*]每次运行应用程序时,它都会rec.pipelinecache在Saved/CollectedPSOs. 收集它们并将它们放在计算机上任意位置的新目录中。本指南使用目录C:\PSOCache.
收集记录的 PSO 缓存时,您的最终目标是查看用户可以在您的应用程序中看到的每一种可能的材料或视觉效果,因此您应该彻底访问尽可能多的位置,并使用许多不同的图形设置组合进行访问。
您不需要在单次运行中收集所有 PSO 缓存。您可以在开发应用程序的过程中执行多次运行,也可以将此任务分配给多人。除非您手动删除记录的 PSO 缓存,否则您可以在开发过程中逐渐积累它们。
3.转换PSO缓存(也称为扩展)

要将前面步骤中的 PSO 数据转换为有用的格式,请执行以下步骤:

[*]烹饪项目的内容。您可以通过打包您的应用程序来做到这一点。
[*]打开(Project)/Saved/Cooked///Metadata/PipelineCaches。将稳定着色器密钥 ( .shk) 文件从此目录复制到您放置rec.pipelinecache文件的文件夹中。例如,C:\PSOCache。
[*]使用以下参数运行ShaderPipelineCacheTools命令行开关(假设您的当前目录是引擎安装目录):
Engine\Binaries\Win64\UnrealEditor-Cmd.exe -run=ShaderPipelineCacheTools expand C:\PSOCache*.rec.upipelinecache C:\PSOCache*.shk C:\PSOCache__.spc
您提供的文件名应由以下内容构成:


[*] – 任意字符串,通常指定收集发生的时间。例如,这可能是当前构建的更改列表。
[*] – 您的项目名称。例如:射手游戏。这必须与您的项目名称完全匹配,否则将不会被拾取。
[*] – 项目的着色器格式,必须与着色器格式的名称完全匹配。对于 D3D12,这是 PCD3D_SM5。

使用上述约定的项目 ShooterGame 的全名如下:CL11122333_ShooterGame_PCD3D_SM5.spc

[*]将.spc上一节中生成的文件放入 Build//PipelineCaches 文件夹中。例如:Build/Windows/PipelineCaches。
[*]再次烹饪或打包您的项目。您的 PSO 缓存文件应该由 cooker 提取,并且您的日志应该在接近尾声时包含如下内容:
LogCook: Display: ---- Running UShaderPipelineCacheToolsCommandlet for platform WindowsClientshader format PCD3D_SM5
LogCook: Display:   With Args: build "../../../TestGame/Build/Windows/PipelineCaches/*TestGame_PCD3D_SM5.spc""d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Metadata/PipelineCaches/ShaderStableInfo-Global-PCD3D_SM5.shk" "d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Metadata/PipelineCaches/ShaderStableInfo-TestGame-PCD3D_SM5.shk" "d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Content/PipelineCaches/Windows/TestGame_PCD3D_SM5.stable.upipelinecache"
LogShaderPipelineCacheTools: Display: Sorting input stable cache files into chronological order for merge processing...
LogShaderPipelineCacheTools: Display: Loading d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Metadata/PipelineCaches/ShaderStableInfo-Global-PCD3D_SM5.shk...
LogShaderPipelineCacheTools: Display: Loading d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Metadata/PipelineCaches/ShaderStableInfo-TestGame-PCD3D_SM5.shk...
LogShaderPipelineCacheTools: Display: Loaded 3554 shader info lines from d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Metadata/PipelineCaches/ShaderStableInfo-Global-PCD3D_SM5.shk.
LogShaderPipelineCacheTools: Display: Loaded 3833694 shader info lines from d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Metadata/PipelineCaches/ShaderStableInfo-TestGame-PCD3D_SM5.shk.
LogShaderPipelineCacheTools: Display: Loaded 3837248 unique shader info lines total.
LogShaderPipelineCacheTools: Display: Loaded 13238 stable PSOs from ../../../TestGame/Build/Windows/PipelineCaches/++Test+GoldMaster-CL-17412694-TestGame_PCD3D_SM5.spc. 2329 PSOs rejected, 5840141 PSOs merged
LogShaderPipelineCacheTools: Display: Re-deduplicated into 35084 binary PSOs .
LogShaderPipelineCacheTools: Display: Running sanity check (consistency of vertex format).
LogShaderPipelineCacheTools: Display: 0 vertex shaders are used with an inconsistent vertex format
LogShaderPipelineCacheTools: Display: === Sanitizing results ===
LogShaderPipelineCacheTools: Display: Before sanitization: ....................................................................35382 PSOs
LogShaderPipelineCacheTools: Display: Filtered out due to inconsistent vertex declaration for the same vertex shader:..........      0 PSOs
LogShaderPipelineCacheTools: Display: Filtered out due to VS being possibly incompatible with an empty vertex declaration:.....      1 PSOs
LogShaderPipelineCacheTools: Display: -----
LogShaderPipelineCacheTools: Display: Number of PSOs after sanity checks:......................................................35381 PSOs
LogShaderPipelineCacheTools: Display: Wrote 35381 binary PSOs (graphics: 34834 compute: 547 RT: 0), (18453KB) to d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Content/PipelineCaches/Windows/TestGame_PCD3D_SM5.stable.upipelinecache
LogCook: Display: ---- Done running UShaderPipelineCacheToolsCommandlet for platform WindowsClient 要验证这是否有效,请检查显示写入的二进制 PSO 数量的行,该行写在日志的最后。此行上的图形 PSO 数必须大于 0。

LogShaderPipelineCacheTools: Display: **Wrote 35381 binary PSOs** (graphics: 34834 compute: 547 RT: 0), (18453KB) to d:/build/++Test/Sync/TestGame/Saved/Cooked/WindowsClient/TestGame/Content/PipelineCaches/Windows/TestGame_PCD3D_SM5.stable.upipelinecache LogCook: Display: ---- Done running UShaderPipelineCacheToolsCommandlet for platform WindowsClient5. 测试 PSO 覆盖率

要确定您的 PSO 缓存是否具有足够的覆盖率,请使用-logpso命令运行新打包的应用程序并观察日志输出。您应该看到类似于以下内容的行:
LogRHI: Opened FPipelineCacheFile: ../../../ShooterGame/Content/PipelineCaches/Windows/ShooterGame_PCD3D_SM5.stable.upipelinecache (GUID: EA50968D47BDE9A04A8524BCEB51615D) with 269 entries.该数量必须与打包日志中写入的二进制 PSO 数量一致。例如,如果日志报告它写入了 35381 个二进制 PSO,则预计会看到 35381 个条目。
您还应该检查构建是否在日志中打印“Encountered a new graphics PSO”。如果您在与收集缓存期间相同的条件(例如,相同的可伸缩性设置)下看到相同的内容,则不应发生这种情况。
分区缓存

在撰写本文时,生成的 PSO 缓存是包含在游戏构建中的单个整体文件。游戏的默认行为是尝试在启动时打开它并从中开始编译 PSO。然而,并非所有 PSO 在任何时候都是相关的。例如,有些可能是从不同的级别收集的,有些是用不同的图形设置记录的。
为了避免不必要地编译所有这些,每个 PSO 目前都与一个称为游戏使用掩码的位掩码相关联。应用程序可以使用该SetGameUsageMaskWithComparison函数来避免编译在不同级别或使用不同图形(质量)设置记录的 PSO。以下是此类函数的示例:
void SetPSOCacheUsageMask(int32 QualityLevel, int32 MapIndex)
{
    uint64 GameMask = 0;
    const int32 kMaxQualityLevels = 4;
    GameMask |= (1ULL << static_cast<uint64>(QualityLevel));
    check(MapIndex < 64 - kMaxQualityLevels);
    GameMask |= (1ULL << static_cast<uint64>(kMaxQualityLevels + MapIndex));
    // default bit-wise AND comparison will work, no need to overload comparison function
    FShaderPipelineCache::SetGameUsageMaskWithComparison(GameMask);
}如果游戏中有超过 60 张地图或者你想编码更精细的质量设置,你可以不同地生成掩码,比如将 uint64 视为几个位域的结构,然后使用自定义比较函数来比较它们。例如:
union
{
    uint64 Packed;

    struct
    {
      uint64 MaterialQuality : 4;
      uint64 ShadowQuality : 4;
      uint64 MapIndex : 16;
    };
};此功能需要在启动早期由应用程序设置,例如加载/保存用户设置时 ( UGameUserSettings)。它用于以下每种情况:

[*]在记录过程中——记录的 PSO 将与当前使用掩码相关联。
[*]在编译期间——只有匹配当前使用掩码的 PSO 才会从缓存中编译。
由于编译开始得非常早,您可能希望默认启动它暂停(见下文)并在设置正确的掩码后显式重新启用。
局限性和未来的工作

目前,口罩的概念本质上依赖于人工采集。相比之下,在 cook 期间自动添加到缓存中的计算 PSO 都具有 0xffffffffffffffff(全为 1)的掩码。实现 PSO 缓存的编程填充意味着也将自动缓存应用于(某些)图形 PSO,并且用户掩码概念可能会演变成不同的方法或完全退出。
如果您的游戏内容被拆分成多个下载,则很难将 PSO 缓存拆分成块以对应独立的内容包(本文档不涉及拆分缓存,而是参考Cooking 和 Chunking)。
控制 PSO 编译

包含在缓存中的 PSO 需要编译,以便在渲染代码需要它们时准备就绪。在使用默认设置的新项目中,应用程序启动后会自动打开一个捆绑的 PSO 缓存文件,并自动开始编译。如果这是不可取的(例如,因为您需要设置自定义使用掩码),您可以使用暂停编译r.ShaderPipelineCache.StartupMode=0,稍后使用FShaderPipelineCache::ResumeBatching().
编译 PSO 时可以使用多种启动模式:

价值模式描述0暂停编译暂停,直到恢复。1个快速地建议在加载屏幕或游戏的其他非交互部分使用快速模式2个背景后台模式更适合在播放器浏览 UI 时进行编译。3个预编译结合了 Fast 和 Background 的属性。使用单独的预编译使用掩码(配置为r.ShaderPipelineCache.PreCompileMask)在快速模式下编译与其匹配的 PSO,但对其余部分使用后台模式(仅匹配常规使用掩码)。
FShaderPipelineCache::NumPrecompilesRemaining()如果游戏想要保持加载屏幕更长时间,直到编译完成, 您还可以通过调用来查询要编译的未完成 PSO 的数量。
模式具有更细粒度的设置,这意味着您可以一次性设置用于预编译的批处理大小以及每帧预编译花费的目标时间。编译 PSO 所需的实际时间不在游戏的控制范围内。
用户缓存文件

即使您为游戏提供 PSO 缓存,用户也可能会遇到收集期间未涵盖的内容。一些驱动程序可以提供自己的缓存,但为了更加独立于驱动程序行为,游戏默认会尝试收集错过的 PSO 并将其保存到本地用户缓存文件中。这些位于游戏的保存目录 ( FPaths::ProjectSavedDir()) 中,该目录与游戏的用户设置相同。应用程序在启动时加载这些用户缓存文件,并将它们的内容与构建中包含的文件合并。
用户缓存 PSO 文件采用记录缓存格式。这意味着他们使用 SHA 哈希值引用着色器,并且无法在更改大量内容的大型游戏更新中使用。因此,每个文件都嵌入了一个游戏版本,该版本会根据正在运行的应用程序进行检查。它在 DefaultGame.ini 中配置,如下例所示,每次应用程序发布可能不兼容的更新时都需要增加,例如包含内容更改或重要渲染代码更改的更新。

GameVersion=1234默认情况下,GameVersion取自EngineVersion,后者通常密切跟踪 Perforce 更改列表,使由任何两个不同构建编写的用户缓存不兼容(即使没有相关更改)。
为了防止缓存文件无限大,应用程序在加载后立即“垃圾收集”其中的条目。这基于条目的最后使用时间,可通过 CVar 进行配置r.ShaderPipelineCache.UserCacheUnusedElementRetainDays(默认值为 30 天)。
Vulkan 和 OpenGL ES RHI 在 RHI 中有自己的低级管道缓存。编译 PSO 后(无论来源是缓存还是由代码创建),它将保存到该缓存中并在下次启动时获取。使用这些图形 API 时可能不需要启用用户缓存文件。
<hr/>我需要多久收集一次 PSO?
理想情况下,您应该在每次以重要方式添加、更新或更改内容时重新捕获项目的 PSO 数据。然而,在实践中,由于发展速度很快,这是不可能的。由于每个项目都有不同的 PSO 收集要求,因此不可能建立严格的时间表。一个经验法则是,如果您觉得您的构建经常开始挂起,则需要更新 PSO 缓存。特别是,永远不要测量没有最新缓存的构建的性能,或者至少不要在比较中使用这些测量结果。
我需要制作特殊的 PSO 缓存级别吗?
虽然您可以从项目的标准级别捕获所有需要的 PSO 数据,但某些项目可能会受益于创建特殊的 PSO 捕获级别。可以设置这些级别,以便它们生成特定类型的所有资产,然后捕获它们的 PSO 数据。如果您的项目包含需要时间解锁或动态生成的内容,则尤其如此。
我没有收集任何 PSO,但我的构建有一个 PSO 缓存!?
如果您有 Niagara 内容,这是意料之中的。计算 PSO 会在烘焙过程中自动添加到缓存中(如果您为项目启用了缓存)。
我的构建中有一个 PSO 缓存,但游戏仍然存在问题。
首先,验证以下内容:

[*]游戏在缓存支持的图形 API 上运行(例如,D3D12,但不是D3D11),
[*]该文件在游戏开始时打开。
[*]正在后台编译正确的 PSO。
为此,请检查日志文件并查找LogRHI日志记录类别。你应该看到这样的行:
LogRHI: Opened FPipelineCacheFile: ../../../TestGame/Content/PipelineCaches/Windows/TestGame_PCD3D_SM5.stable.upipelinecache (GUID: 91C5586843C2B5CEE3F4F7BE47E71253) with 908 entries.

LogRHI: Display: Opened pipeline cache after state change and enqueued 908 of 908 tasks for precompile.条目和任务的数量会有所不同,但不应为 0。
其次,检查有多少新的 PSO 被发现。为此,使用-logPSO命令行开关(或启用用户缓存文件)运行构建,并查看您遇到这样一行的频率:
LogRHI: Display: Encountered a new graphics PSO: 4233039161后面给出的数字PSO:会有所不同。如果您看到大量计数并且它们的外观与故障一致,则您的缓存可能有问题。请对当前内容重新收集缓存。您甚至可能需要从头开始。
如果您没有这些行,或者您正在 D3D11 上运行,但您的构建仍然存在问题,则问题可能与 PSO 没有直接关系。在这种情况下,我们建议使用 CPU 分析器对游戏进行分析,以了解故障的性质。
本地配置不够,如何解决Unreal渲染速度的问题?

Unreal虚幻引擎电脑配置的要求是比较高,特别是实时渲染,前期的硬件成本是比较高,这里有一个简单的节省硬件成本的方法,使用赞奇超高清云工作站,即使本地普通的电脑也能运行UE5软件,且普通电脑也能享受行业最高端的CPU和GPU,极大提高制作效率和使用体验,且使用方便快捷,全面支持3D应用软件插件运行,随时调用百余款软件插件,高效作业。
赞奇超高清云工作站可以为UNREAL 用户提供云端制作输出方案,提高工作效率。为Unity用户提供灵活、高效、低成本的云端烘焙服务,享受游戏制作的乐趣。用户在全国各地通过赞奇超高清云工作站直接连接服务器,共享一套资产, 可以直接在赞奇超高清云工作站上制作 / 修改工程文件,减少数据传输成本,高性能云办公选赞奇超高清云工作站!
赞奇超高清云工作站支持专业10bit色深、YUV444真彩无损显示。4K60帧的高清显示,让设计师处理高速动态视频时体验依然流畅。动画设计、影视特效等内容云端制作,快人一步。赞奇超高清云工作站行业领先配置、多规格高性能GPU机型,可秒级启动百万核算力资源,万核渲一图。极致稳定的计算性能,满足单集群上万台机器并发渲染,可以满足移动办公、人工智能、工业仿真、三维建模、视觉设计、影视制作、教育培训、动画渲染、娱乐畅玩等多场景应用。
页: [1]
查看完整版本: unreal 5.0中可用的 PSO 类型以及生成 PSO 缓存的详细过程 ...