异步着色器编译可以在编译新的着色器变体时渲染虚拟着色器,从而防止 Unity Editor 停顿。默认情况下,在 Editor 设置(菜单:__Edit > Project Settings…> Editor > Shader Compilation__)中已启用异步着色器编译。
着色器可以包含成百上千个变体,这些变体涵盖具有不同关键字组合的不同使用场景。如果在加载着色器时 Editor 必须编译所有变体,则着色器导入过程将非常缓慢。 为了处理此问题,Unity 在默认情况下使用_按需着色器编译。这意味着 Editor 在场景中首次遇到着色器变体时会编译这些着色器变体。 这种情况下会使 Editor 中的渲染停顿,因为完整的着色器编译可能需要数毫秒到数秒的时间。编译着色器变体所需的时间取决于所选的图形 API 和着色器的复杂性。要克服这些停顿问题,请使用__异步着色器编译_。
默认情况下已启用异步着色器编译。可以针对 Game 视图和 Scene 视图禁用此功能。如果不知道渲染解决方案的哪些部分导致了问题,这样做将很有用。但是,在着色器编译期间,可能仍然会在 Editor 中遇到渲染停顿的情况。
要禁用异步着色器编译,请执行以下操作:
1) 在项目中,选择 Edit > Project Settings…> Editor。
2) 在 Editor 设置底部的 Shader Compilation 下,取消选中 Asynchronous Shader Compilation 复选框。
注意:禁用异步着色器编译后,该设置仅影响 Game 视图和 Scene 视图。该设置不会影响显式启用异步着色器编译的系统和自定义脚本。
通过异步着色器编译,Editor 可以将着色器变体添加到编译队列中,并在作业线程中完成这些着色器变体,而不会立即编译这些变体。.为了避免渲染停顿,Editor 在编译着色器变体的同时继续进行渲染。然后,Editor 使用纯青色的虚拟着色器来填充着色器变体的位置(偶尔会短暂看到青色虚拟着色器,显示后台正在进行的操作)。这样可避免因为 Editor 仍在编译某些对象而未在场景中渲染这些对象。编译完成后,Editor 将换入真实着色器。Editor 右下角的进度条会显示编译队列的状态。
该功能对独立平台播放器没有任何影响,因为 Editor 会在构建过程中编译播放器所需的所有着色器变体。
注意:默认情况下,异步着色器编译功能在 Game 视图和 Scene 视图中有效。如果希望将此功能用于其他情况,请参阅在自定义 Editor 工具中使用异步着色器编译。
注意:如果使用了 DrawProcedural
或 CommandBuffer.DrawProcedural
,Editor 不会换入虚拟着色器。Editor 只是跳过对此着色器变体的渲染,直到此着色器变体的编译完成为止。
注意:Blit 操作不会使用异步着色器编译。这是为了保证在最常见的用例中获得正确输出。
默认情况下,异步着色器编译功能在 Scene 视图和 Game 视图中有效。高级渲染解决方案依赖于一次生成数据并在以后的帧中进行复用,这可能会导致虚拟着色器污染已生成的数据。如果发生此情况,即使在着色器完成编译后,也可能会在场景中看到青色或其他渲染瑕疵。只有在 Editor 第一次遇到生成数据的着色器变体时才会发生此问题。但是,为了避免这些问题,可以针对渲染的某些部分禁用异步着色器编译,对某些着色器强制进行同步着色器编译,或者在编译完成后检测特定的数据污染情况并重新生成数据。
如果要使用异步着色器编译,但又不希望虚拟着色器在特定渲染调用中影响渲染结果,则可以在 C# 脚本中针对这些调用禁用此功能。以下说明展示了如何在直接作用域和 CommandBuffer 作用域中禁用该功能。在这两种情况下,实际上就是在不想让 Unity 异步编译的渲染调用周围注入钳制。
在_直接作用域_中,必须使用 ShaderUtil.allowAsyncCompilation;
。
将 ShaderUtil.allowAsyncCompilation
的当前状态保存到变量。
在调用要禁用异步编译的渲染命令之前,立即将ShaderUtil.allowAsyncCompilation
设置为 false
。
在希望影响的渲染调用之后,将 ShaderUtil.allowAsyncCompilation
恢复到步骤 1 中保存的前一个状态。
下面是一个伪代码示例:
// 存储当前状态并禁用异步编译
bool oldState = ShaderUtil.allowAsyncCompilation;
ShaderUtil.allowAsyncCompilation = false;
// 输入绝不应使用虚拟着色器的渲染代码
Graphics.DrawMesh(...);
// 恢复旧状态
ShaderUtil.allowAsyncCompilation = oldState;
在 CommandBuffer 作用域中,必须使用 ShaderUtil.SetAsyncCompilation
和 ShaderUtil.RestoreAsyncCompilation
。
在调用要禁用异步编译的渲染命令之前,立即调用 ShaderUtil.SetAsyncCompilation
并将其设置为 false
。缓冲区中所有紧随其后的命令不会允许异步编译。
在希望影响的渲染命令之后,使用 Shader.Util.RestoreAsyncCompilation
。
下面是一个伪代码示例:
// 禁用随后命令的异步编译
ShaderUtil.SetAsyncCompilation(cmd, false);
/// 输入绝不应使用虚拟着色器的渲染命令
cmd.DrawMesh(...);
// 恢复旧状态
ShaderUtil.RestoreAsyncCompilation(cmd);
可以针对特定着色器强制进行同步编译。如果生成数据的着色器在渲染开始时始终存在且编译速度相对较快,这是一个很好的选择。
要针对特定着色器强制进行同步编译,请执行以下操作:
在着色器代码中,添加此指令:
#pragma editor_sync_compilation
注意:对于在渲染过程中会遇到新变体的复杂着色器,不要强制进行同步编译。对复杂着色器进行强制同步编译可能会使 Editor 中的渲染停顿。
如果虚拟着色器污染了生成的数据,必须丢弃污染的数据,并使用正确编译的着色器来重新生成一组新数据。
如果已经知道 Unity 用于生成数据的材质:
请使用 ShaderUtil.IsPassCompiled
检查由材质当前状态指定的着色器变体编译状态。
该状态从 Uncompiled 变为 Compiled 时,请刷新所生成的数据。
如果不知道哪个具体材质会生成污染的数据,或者污染数据的生成并非与单个材质相关:
请使用 ShaderUtil.anythingCompiling
检测 Unity 是否正在异步编译任何着色器。
任何异步编译完成后,请刷新数据。
默认情况下,异步着色器编译功能在 Game 视图和 Scene 视图中有效。如果要在自定义 Editor 工具中使用该功能,可以通过 C# 为自定义工具启用该功能。
在_直接作用域_中,必须使用 ShaderUtil.allowAsyncCompilation;
。
将 ShaderUtil.allowAsyncCompilation
的当前状态保存到变量。
在调用要启用异步编译的渲染命令之前,立即将ShaderUtil.allowAsyncCompilation
设置为 true
。
在希望影响的渲染调用之后,将 ShaderUtil.allowAsyncCompilation
恢复到步骤 1 中保存的前一个状态。
下面是一个伪代码示例:
// 存储当前状态并启用异步编译
bool oldState = ShaderUtil.allowAsyncCompilation;
ShaderUtil.allowAsyncCompilation = true;
// 输入希望使用异步编译的渲染代码
Graphics.DrawMesh(...);
// 恢复旧状态
ShaderUtil.allowAsyncCompilation = oldState;
在基于 CommandBuffer 的作用域中,必须使用 ShaderUtil.SetAsyncCompilation
和 ShaderUtil.RestoreAsyncCompilation
。
在调用要启用异步编译的渲染命令之前,立即调用 ShaderUtil.SetAsyncCompilation
并将其设置为 true
。缓冲区中所有紧随其后的命令随后会允许异步编译。
在希望影响的渲染命令之后,使用 Shader.Util.RestoreAsyncCompilation
。
下面是一个伪代码示例:
// 启用随后命令的异步编译
ShaderUtil.SetAsyncCompilation(cmd, true);
/// 输入希望使用异步编译的渲染命令
cmd.DrawMesh(...);
// 恢复旧状态
ShaderUtil.RestoreAsyncCompilation(cmd);
Graphics.ExecuteCommandBuffer(cmd);
可以使自定义工具为每种材质绘制非虚拟着色器的内容。这样就可以避免使用会变为青色的着色器变体来进行渲染,而是在着色器编译时绘制其他内容。
要检查是否已编译特定着色器变体,请使用 ShaderUtil.IsPassCompiled
。
要手动触发编译,请使用 ShaderUtil.CompilePass
。这样就可以避免使用会变为青色的着色器变体来进行渲染,而是在着色器编译时绘制其他内容。
Did you find this page useful? Please give it a rating:
Thanks for rating this page!
What kind of problem would you like to report?
Thanks for letting us know! This page has been marked for review based on your feedback.
If you have time, you can provide more information to help us fix the problem faster.
Provide more information
You've told us this page needs code samples. If you'd like to help us further, you could provide a code sample, or tell us about what kind of code sample you'd like to see:
You've told us there are code samples on this page which don't work. If you know how to fix it, or have something better we could use instead, please let us know:
You've told us there is information missing from this page. Please tell us more about what's missing:
You've told us there is incorrect information on this page. If you know what we should change to make it correct, please tell us:
You've told us this page has unclear or confusing information. Please tell us more about what you found unclear or confusing, or let us know how we could make it clearer:
You've told us there is a spelling or grammar error on this page. Please tell us what's wrong:
You've told us this page has a problem. Please tell us more about what's wrong:
Thank you for helping to make the Unity documentation better!
Your feedback has been submitted as a ticket for our documentation team to review.
We are not able to reply to every ticket submitted.