Version: 1.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 objectAn instance of the Shader class, a Shader object is container for shader programs and GPU instructions, and information that tells Unity how to use them. Use them with materials to determine the appearance of your scene. More info
See in Glossary
, 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 恢复到之前的状态。

下面是一个伪代码示例:

// Store the current state
bool oldState = ShaderUtil.allowAsyncCompilation;

// Disable async compilation
ShaderUtil.allowAsyncCompilation = false;

// Enter your rendering code that should never use the placeholder shader, for example UI elements or characters.
Graphics.RenderMesh(...);

// Restore the old state
ShaderUtil.allowAsyncCompilation = oldState;

在 CommandBuffer 作用域中

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

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

下面是一个示例:

// Create the CommandBuffer
CommandBuffer cmd = new CommandBuffer();

// Disable async compilation for subsequent commands
ShaderUtil.SetAsyncCompilation(cmd, false);

/// Enter your rendering commands that should never use the placeholder shader, for example UI elements or characters.
cmd.DrawMesh(...);

// Restore the old state
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