Version: 2021.3
着色器编译
Branching, variants, and keywords

异步着色器编译

异步着色器编译是一项仅限于编辑器的功能,当您的 Shader 对象比较复杂,包含大量着色器变体时,此功能可以改进您的工作流程。

本页包含以下信息:

概述

Shader objects can contain of hundreds or thousands of shader variants. If the Editor compiled all variants when loading a Shader object, the import process would be very slow. Instead, the Editor compiles shader variants on-demand, the first time it encounters them.

编译这些着色器变体可能会导致编辑器停顿几毫秒甚至几秒,具体取决于图形 API 和 Shader 对象的复杂性。为了避免这些停顿,您可以使用异步着色器编译在后台编译着色器变体,同时使用占位 Shader 对象。

异步着色器编译的工作原理

异步着色器编译的工作原理是:

  1. 当编辑器第一次遇到未编译的着色器变体时,它会将着色器变体添加到作业线程上的编译队列中。编辑器右下角的进度条会显示编译队列的状态。
  2. 在加载着色器变体时,编辑器使用占位着色器渲染几何体,该着色器显示为纯青色。
  3. 当编辑器完成对着色器变体的编译后,它会使用着色器变体来渲染几何体。
Unity 会渲染仍在用青色虚拟着色器进行编译的着色器变体,直到编译完成。右下角的进度条指示编译队列的进度。
Unity 会渲染仍在用青色虚拟着色器进行编译的着色器变体,直到编译完成。右下角的进度条指示编译队列的进度。

异常

将产生以下异常:

如果您使用 DrawProcedural 或者 CommandBuffer.DrawProcedural,编辑器不使用占位着色器。编辑器只是跳过对此几何体的渲染,直到此着色器变体的编译完成为止。 Unity 编辑器不会使用带有 Blit 操作的异步着色器编译。这是为了保证在最常见的用例中获得正确输出。

为您的项目启用和禁用异步着色器编译

默认情况下已启用异步着色器编译。

要利用或禁用异步着色器编译,请执行以下操作:

  1. 转到 Edit > Project Settings > Editor
  2. 在编辑器设置底部的 Shader Compilation 下,取消选中 Asynchronous Shader Compilation 复选框。
可以在 Project Settings > Editor > Shader Compilation 下找到 Asynchronous Shader Compilation 的复选框。
可以在 Project Settings > Editor > Shader Compilation 下找到 Asynchronous Shader Compilation 的复选框。

注意:默认情况下,以这种方式启用和禁用异步着色器编译仅影响场景和游戏视图。如果希望将此功能用于编辑器的其他部分,请参阅自定义编辑器工具和异步着色器编译

为特定渲染调用启用和禁用异步着色器编译

您可以在 C# 脚本中为特定渲染命令启用或禁用异步着色器编译。

以下说明展示了如何在直接作用域和 CommandBuffer 作用域中禁用该功能。

在直接作用域中

在直接作用域内,您可以使用 ShaderUtil.allowAsyncCompilation

为此需要执行以下操作:

  1. ShaderUtil.allowAsyncCompilation 的当前状态存储在一个变量中。
  2. 在调用渲染命令之前,将 ShaderUtil.allowAsyncCompilation 设置为 false
  3. 调用渲染命令。
  4. 调用渲染命令后,将 ShaderUtil.allowAsyncCompilation 恢复到之前的状态。

下面是一个伪代码示例:

// 存储当前状态
bool oldState = ShaderUtil.allowAsyncCompilation;

// 禁用异步编译
ShaderUtil.allowAsyncCompilation = false;

// 输入绝不应使用占位着色器的渲染代码
Graphics.DrawMesh(...);

// 恢复旧状态
ShaderUtil.allowAsyncCompilation = oldState;

在 CommandBuffer 作用域中

CommandBuffer 作用域内,您可以使用 ShaderUtil.SetAsyncCompilationShaderUtil.RestoreAsyncCompilation

  1. 在调用渲染命令之前,调用 ShaderUtil.SetAsyncCompilation,并将其设置为 false。CommandBuffer 中的后续命令将不允许异步编译。
  2. 将渲染命令添加到 CommandBuffer。
  3. 在渲染命令之后,调用 Shader.Util.RestoreAsyncCompilation 恢复异步着色器编译的状态。

下面是一个示例:

// 创建 CommandBuffer
CommandBuffer cmd = new CommandBuffer();

// 禁用后续命令的异步编译
ShaderUtil.SetAsyncCompilation(cmd, false);

///  输入绝不应使用占位着色器的渲染命令
cmd.DrawMesh(...);

// 恢复旧状态
ShaderUtil.RestoreAsyncCompilation(cmd);

为特定 Shader 对象禁用异步着色器编译

通过强制编辑器始终进行同步编译,您可以为特定 Shader 对象禁用异步着色器编译。如果生成数据的 Shader 对象在渲染开始时始终存在且编译速度相对较快,这是一个很好的选择。如果您正在执行高级渲染 ,很可能需要这样做。

要为特定 Shader 对象强制进行同步编译,请将 #pragma editor_sync_compilation 指令添加到您的着色器的源代码中。

注意:对于在渲染过程中会遇到新变体的复杂 Shader 对象,不要强制进行同步编译;这种情况下会使编辑器中的渲染停顿。

检测异步着色器编译

您可以使用 C# API 监控异步着色器编译状态,并在此状态更改时执行操作。

这很可能会在高级渲染中有用。如果占位着色器污染了您生成的数据,您可以等到编译完成,丢弃污染的数据,并使用正确的着色器变体重新生成新的数据集。

如果您已知自己感兴趣的材质,可以使用 ShaderUtil.IsPassCompiled 检查着色器变体的编译状态。当状态从 Uncompiled 改变为 Compiled 时,编译完成。

如果您不知道自己感兴趣的材质,或者您对不止一种材质感兴趣,可以使用 ShaderUtil.anythingCompiling 检测 Unity 是否正在异步编译任何着色器变体。当状态从 true 变为 false 时,所有当前编译完成。

编辑器中的高级渲染和异步着色器编译

高级渲染解决方案依赖于生成一次数据并在以后的帧中重复使用。如果编辑器在此过程中使用占位着色器,则可能会污染生成的数据。如果发生此情况,即使在着色器变体完成编译后,也可能会在场景中看到青色或其他渲染瑕疵。

为避免这种情况,您可以:

自定义编辑器工具和异步着色器编译

默认情况下,异步着色器编译功能在 Game 视图和 Scene 视图中有效。如果要在自定义 Editor 工具中使用该功能,可以通过 C# 为自定义工具启用该功能。

为此,您可以为特定的渲染调用启用异步着色器编译

自定义编译时间渲染

可以使自定义工具为每种材质绘制非占位着色器的内容。这样就可以避免使用纯青色来进行渲染,而是在着色器变体进行编译时绘制其他内容。

要检查特定着色器变体是否已编译,请参阅检测异步着色器编译

要手动触发编译,您可以使用 ShaderUtil.CompilePass。这样就可以避免使用青色占位着色器来进行渲染,而是在着色器变体进行编译时绘制其他内容。

着色器编译
Branching, variants, and keywords