Version: 2023.2
着色器资源
异步着色器编译

着色器编译

概述

每次构建项目时,Unity 编辑器都会编译构建所需的所有着色器:针对每个所需的图形 API 编译每个所需的着色器变体。

当您在 Unity 编辑器中工作时,编辑器不会提前编译所有内容。这是因为为每个图形 API 编译每个变体可能需要很长时间。

相反,Unity 编辑器会这样做:

  • 当导入一个着色器资源时,会执行一些最小的处理(例如表面着色器生成)。
  • 当需要显示着色器变体时,它会检查 Library/ShaderCache 文件夹。
  • 如果找到使用相同源代码的先前编译的着色器变体,则会使用该着色器变体。
  • 如果没有找到匹配项,则编译所需的着色器变体并将结果保存到缓存中。
    • 注意:如果您启用异步着色器编译,它在后台执行此操作并同时显示占位着色器。

着色器编译使用名为 UnityShaderCompiler 的进程。可启动多个 UnityShaderCompiler 编译器进程(通常在机器中每个 CPU 核心对应一个),这样在播放器构建时就可以并行完成着色器编译。当编辑器不编译着色器时,编译器进程不执行任何操作,也不消耗计算机资源。

如果有许多经常更改的着色器,着色器缓存文件夹可能会变得非常大。删除此文件夹是安全的;只会导致 Unity 重新编译着色器变体。

在播放器构建时,所有“尚未编译”的着色器变体都将被编译,因此即使编辑器不会使用这些着色器变体,它们也会存在于游戏数据中。

不同的着色器编译器

不同平台使用不同的着色器编译器来编译着色器程序,如下所述:

  • 使用 DirectX 的平台会使用 Microsoft 的 FXC HLSL 编译器。
  • 使用 OpenGL (Core & ES) 的平台会使用 Microsoft 的 FXC HLSL 编译器,然后使用 HLSLcc 将字节代码转换为 GLSL。
  • 使用 Metal 的平台会使用 Microsoft 的 FXC HLSL 编译器,然后使用 HLSLcc 将字节代码转换为 Metal。
  • 使用 Vulkan 的平台会使用 Microsoft 的 FXC HLSL 编译器,然后使用 HLSLcc 将字节代码转换为 SPIR-V。
  • 其他平台(如游戏主机平台)使用其各自的编译器。
  • 表面着色器使用 HLSL 和 MojoShader 来完成代码生成分析步骤。

使用 pragma 指令可以配置各种着色器编译器设置。

缓存着色器预处理器

着色器编译涉及几个步骤。首先,需要进行预处理。在此步骤中,一个名为预处理器的程序为编译器准备着色器源代码。

In previous versions of Unity, the Editor used the preprocessor provided by the shader compiler for the current platform. Now, Unity uses its own preprocessor, also called the Caching Shader Preprocessor.

The Caching Shader Preprocessor is optimized for faster shader import and compilation. It works by caching intermediate preprocessing data, so the Editor only needs to parse include files when their contents change, which makes compiling multiple variants of the same shader more efficient.

有关缓存着色器预处理器和以前行为之间差异的详细信息,请参阅 Unity 论坛:新着色器预处理器 (New shader preprocessor)

AssetBundles and shaders

If you use AssetBundles, Unity might compile duplicate shaders if you reference one shader in two or more objects. For example:

  • A material in an AssetBundle and a material in a built scene reference the same shader.
  • Multiple AssetBundles contain materials that reference the same shader outside an AssetBundle.

This can increase the memory and storage space shaders use, and break draw call batching.

To avoid this, you can use the following approaches:

  • Load an AssetBundle that contains all your shaders first, then load and instantiate AssetBundle assets that reference the shaders. See AssetBundle Dependencies for more information.
  • Structure your AssetBundles to minimise duplication. See Asset Duplication for more information.

You can add materials and shader variant collections to an AssetBundle to specify which shader variants to include.

If you create a single AssetBundle, some shaders might stay in memory even if they’re no longer needed, because you cannot partially unload an AssetBundle. You can avoid this by creating a separate AssetBundle for each group of shaders you use together, for example a ‘forest’ AssetBundle and a ‘desert’ AssetBundle. See Managing loaded AssetBundles, or Memory management in the Addressables system if you use Addressables.

You can use the Asset Bundle Browser to check which assets in AssetBundles depend on other assets, and find out if any assets are duplicated.

构建时剥离

While building the game, Unity can detect that some of the internal shader variants are not used by the game, and exclude (“strip”) them from build data. For more information, see Shader variants.

着色器资源
异步着色器编译