着色器编译是一个较为冗长的过程,需要分多步完成。其中,对着色器源数据的预处理是最初几个步骤之一。Unity 2020.1 beta推出了一个全新的编译方案,缓存着色预处理器。与平台自带的编译预处理器相比,新的方案速度要快25%,代码严格遵循C语言规范,且带有许多新功能。
所有Unity使用者都直接或间接接触过着色器。它们是运行于GPU上的特定程序,决定对象在屏幕上渲染出的样子。比如,当场景中添加进了一个点光源,受到影响的对象受到的光照不同,对应的着色器变量也不同。
当引擎需要使用一个新的着色器变量时,需要先经由编辑器编译。而在为目标设备构建应用时,编辑器需要编译所有用到的着色器。
在编辑器设置的着色器编译部分,可以勾选缓存着色预处理器来启用/禁用功能。
本文将介绍功能的技术细节,为着色器编写提供可借鉴之处。
为效率而生
新的预处理器可缓存预处理数据,加快着色器导入和编译的时间。在缓存后,编辑器只在文件内容改动时需要做比对,并且编译同个着色器的多个变量时效率会更高。在项目中,如果着色器大量使用了常见引用文件,新的预处理器可以有最为出色的效果。
统一化
不同的着色编译器后端带有各自的预处理器。在编译前执行标准化预处理层可以让着色器代码兼容任何编译后端,也能让Unity项目的升级更加轻松。
现在着色器在导入时会使用同样的标准化预处理器,替代了此前用于检查着色器编译参数的文本对照方案(#pragma编译指令集)。藉此,我们可以开发出更多的功能,比如条件式#pragma编译指令集,还能将指令写入引用文件中。
全新功能
显示预处理原数据
着色检视器现在带有一个“Preprocess only(仅显示预处理数据)”选框,勾选后可显示预处理后的着色器源数据,不勾选则会显示编译后的着色器代码。功能对着色器的开发和调试作用巨大。
在引用文件中显示着色器编译参数
我们加入了一个新的预处理器指令:#include_with_pragmas,开发者能在文件中使用该指令来控制编译设置。指令的作用与普通的#include指令类似,且不会在导入时被忽视。
该改动可以缩短着色器的导入时间;不然,着色器所包含的每个引用文件都需要被预处理一遍。
旧版着色器导入路径会忽略新的指令。
条件式着色编译指令集
在导入着色器时预处理数据现在可以实现着色器编译指令的打包。
并非所有条件都能影响#pragma编译指令集。预处理器会先检测条件表达式的参数来源,防止出现重复检测,尽可能不打断着色器编译管线。条件仅在#pragma指令依赖于泛型宏定义、自定宏定义或平台关键字(根据平台而定,不能以其他方法设置)时会影响到报告。关键字包括SHADER_API_DESKTOP、SHADER_API_MOBILE、UNITY_NO_CUBEMAP_ARRAP、UNITY_FRAMEBUFFER_FETCH_AVAILABLE、UNITY_USE_NATIVE_HDR、UNITY_NO_RGBM以及主机的SHADER_API_<CONSOLE>关键字,这些关键字需根据目标平台而改变。注意自定宏定义不包括在#pragma multi_compile和#pragma shader_feature指令集的自定关键字中。
可行代码示例:
#pragma vertex vert #if defined(SHADER_API_XBOXONE) || defined(SHADER_API_PS4) #pragma fragment ConsoleFrag #elif defined(SHADER_API_SWITCH) || defined(SHADER_API_MOBILE) #pragma fragment MobileFrag #else #pragma fragment DesktopFrag #endif
不可行代码示例:
#pragma multi_compile __ A B #pragma vertex vert #if defined(A) #pragma fragment FragA #elif defined(B) #pragma fragment FragB #else #pragma fragment Frag #endif
预处理行为改动
新的着色器预处理方案有以下几个行为改动:
- 现在支持#pragma warning警告
- 非标准格式的#include指令参数现在可以使用宏来扩展(“#include
或“#include “my_include_file””) - 非标准格式的#line指令参数现在可使用宏来扩展(“#line”后为无符号整数或文件名)
- 宏在重新定义后,如果新定义与先前不同,系统会发出警告
- 宏扩展将严格遵循C语言规范
- 如果指令第一个参数为标识符,不工整的#undef指令照样会运行
- 现在完全支持字符串化(Stringizing)
- 串联(Concatenation)现在无需参数就能用于宏中
- 无效的串联会触发一次警告
- 当用于字符串化或串联时,宏参数将不能再扩展
- 移位运算符现在支持条件表达式
- 预处理器在处理无参数的宏时,不会生成错误报告,不会扩展宏
- “\\\n”(句首的反斜杠)将不再视作空格
旧版着色器导入管线现在归于UNITY_OLD_PREPROCESSOR宏中,用于区分新旧两个预处理器。
本文转自:Unity官方平台,转载此文目的在于传递更多信息,版权归原作者所有。