UE4 Rendering Part 5: Shader Permutations
原文地址:https://medium.com/@lordned/unreal-engine-4-rendering-part-5-shader-permutations-2b975e503dd4就像我们之前介绍过的那样,Unreal会根据不同用途将一个shader/material编译成多种permutation(组合)。当一个Material被修改后,Unreal会去寻找被material使用的.ush/.usf文件,并重新加载它们。接着,Unreal会把这些Material Graph转换成HLSL代码,开始编译shader的Permutation。
我们对于这个应该比较熟悉了,因为在使用Material Editor时经常会遇到。但是,这里还有一个问题:它仅仅是重新加载与material相关的shader吗?由于Unreal使用deferred renderer,所以一些shader是全局的,例如采样GBuffer和计算最终颜色的pass。这也就意味着这些shader不是material的一部分,所以修改这个material,并不能导致他们被重新加载。
另外,一旦你修改这些global shader,当Unreal重启时,它就会重新编译所有依赖global shader文件的所有shader。如果你修改了一个common shader,你可能需要为一个空工程重新编译125 shader和10000 permutations。另外,如果你修改了C++中Renderer模块,你也会遇到类似的麻烦。这种修改会导致你可能需要10分钟来重新编译代码。更可怕的是,如果你修改shader中#if x_y部分内容,你可能需要通过一点点编译来完成整个编译过程。
Lowering the Overall Number of Permutations
我们的第一个目标就是看怎么通过修改engine中的设置,或者优化materials的设置,来减少需要编译的permutation数量。当然,我们不是建议你在ship时候将这些设置Disable,而是让你在开发shader时,通过Disable这些设置,暂时性减少编译permutation的数量,从而提高开发效率。
在per-material basis层面上,有几个地方可以优化。第一个就是Usage设置。
首先来看下我们改变哪些设置会让permutation编译数量减少。这些设置看起来是代表engine中特定的vertex factory。也就是说,如果你在一个新的vertex factory中,选中Automatically Set Usage in Editor,就会导致产生permutation。关闭它,就会让你有多一点控制,让artist再考虑下如何为material赋值。但是,即使这样也不会减少很多permutation。
接下来,你可以看看Quality and Static 开关的使用。任何可能的Quality and Static组合都需要新的permutation。因此,大量的static开关会导致大量的permutation。当然,这种方法也不会大量地减少permutation。另外,还有点事情你要记住,如果你是在尝试迭代,或者面对着空场景,你可能不需要设置它们。但是,如果是在runtime时候,static开关会提高性能的。
最后,我们来看下通过project setting来减少permutation。长期来看你是需要它们,但是我们也能暂时取消它们。当然,这需要Unreal 4.19版本以上。如果你去Project Setting > Render,那里有几个参数可以调整。当然,这还是要取决于你的工程或者测试需要,你可以保留一些设置为有效状态。如果当你在这里取消shader permutation,场景由于缺少需要的permutation,就会在viewport抛出warning。例如,当你尝试使用atmospheric fog,而Support Atmospheric Fog设置无效时,Unreal就会抛出”PROJECT DOES NOT SUPPORT ATMOSPHERIC FOG”。
Lighting
[*]Allow Static Lighting:如果设置为无效,则意味着Static Light对你的material没有影响。
Shader Permutation Reduction
[*]Support Stationary Skylight:如果设置成无效,则意味着stationary sky light对你的material没有影响。
[*]Support Low Quality Lightmap Shader Permutations
[*]Support PointLight WholeSceneShadows
[*]Support Atmospheric Fog
Mobile Shader Permutation Reduction
[*]Support Combined Static and CSM Shadowing
[*]Support Distance Field Shadows
[*]Support Movable Directional Lights
[*]Max Movable Point Lights = 0
将这些都设置成无效后,打开ConsoleVariable.ini添加r.ForceDebugViewModes=2,来进一步减少permutation数量。当然,这也会导致editor中的buffer visualization被取消掉。
通过这些操作,你可以将shader从8809减少到3708(相当于shader减少了58%)。这个效果是非常显著的。
Speeding up Compilation
你可以在编译环境中添加CFLAG_StandardOptimization变量,保证D3D Compiler不花费时间优化shader。为所有shader添加该设置的最简单办法就是,打开ShaderCompiler.cpp,找到GlobalBeginCompileShader函数,添加上Input.Environment.CompilerFlags.Add(CFLAG_StandardOptimization);当然,当你正在优化程序性能时,千万不要这样去做。这个与r.shaders.Optimize=0是不同的。R.shaders.optimize主要是关于debug信息的,而不是代码优化。
Recompiling Only Changed Shaders
我们来看一下重新编译一些特定shader的技巧。Unreal有个没有列出来的console command “recompileshaders”。必须要注意的是,它只能对.usf文件起作用,因为.ush是唯一被.usf所包含的头文件。
[*]Recompileshaders changed查询FShaderType和FShaderPipelineType得到所有过期的shader, factory和shader pipeline。如果查到任何过期的,则重新编译。这也就是意味着global shader在这个命令中也会被编译。实际上,我发现它会重新编译所有受影响的global shader。修改象ShadingModels.ush文件需要3-5分钟的编译。
[*]Recompileshaders global得到global shader map,清空它,然后强制重新编译所有的global shaders。修改象ShadingModels.ush需要1-2分钟。当然,是否知道你正在修改的shader是global的,也是非常重要的。
[*]Recompileshaders material <MaterialName>则将重新编译与名字匹配的第一个material。此时,不需要include任何路径信息。它通过调用material的PreEditChange/PostEditChange事件来完成。所以,这跟点击Material Editor中的Apply按钮是一样的。
[*]Recompileshaders all将会编译所有东西。这个跟修改所有文件后,重新启动editor是一样的。这个可能会在修改即将结束时,用于确保permutation不是broken的,是非常有用的。Project Setting > Rendering Overrides (Local) > Force All Shader Permutation Support会保证你的代码在每个#if variation下编译。
[*]Recompileshaders<path>尝试按shadertype或者shader platform type来重新编译一个shader。这个允许我们指定shader路径,例如recompileshaders/Engine/Private/BasePassPixelShader.usf。
重要提示:这些命令看来是对大小写敏感的。无论是运行上面的哪种命令,这个命令都需要一点时间。不正确的路径或者使用不正确的大小写,都会导致在console上输出一些错误信息,但是它不会重新编译shader。
当使用这些命令时,打开新的map和material将会非常不稳定。因此,要保证你的测试环境是建立好的。它对于崩溃情况也是OK的。因为运行这些命令时,再打开另外一个material或者scene,发现崩溃是非常可能的。
另外,它只对于场景中处于actively的代码部分是有用的。也就是说,如果你只修改了对lit translucency起作用的部分,而在场景却没有translucent物体,hot-reload不会检查这段代码,会忽略过去。
下面是非常有用的两个命令,它只需要3-5秒来运行。他们就是recompileshaders /engine/private/BasePassPixelShaders.usf和recompileshaders /Engine/Private/DeferredLightPixelShader.usf。这两个命令基本上也就是涵盖了你在base pass中所作的大多数修改。
Common #if Defines
下面是在deferred shading pipeline中几个非常常用的pre-processor定义。我已经按照它们的内在意思整理成一个列表。这用于帮助你找出deferred pipeline哪些部分应用到你的代码上,因为你可以通过这个列表, 来检查pre-processor,看看哪些被转换UE4设置了。
[*]#if NON_DIRECTIONAL_DIRECT_LIGTHING。这个定义在DeferredLightingCommon.ush中有,而在ForwardLightingCommon.ush却被定义成#define NON_DIRECTIONAL_DIRECT_LIGHTING(TRANSLUCENCY_LIGHTING_VOLUMETRIC_NONDIRECTIONAL || TRANSLUCENCY_LIGHTING_VOLUMETRIC_PERVERTEX_NONDIRECTIONAL)。
[*]#if SUPPORT_CONTACT_SHADOWS。它提供Unreal Contact Shadow Feature支持。
[*]#if REFERENCE_QUALITY。它在DeferredLightingCommon.ush顶部被定义成0,可能用于电影渲染?
[*]#if ALLOW_STATIC_LIGHTING。 如果r.AllowStaticLighting设置成1,它就会是true。这个跟Project Settings > Rendering中的Static Lighting support选项相对应。
[*]#if USE_DEVELOPMENT_SHADERS。如果COMPILE_SHADER_FOR_DEVELOPMENT为true(同时平台也支持),它就会是true。当然如果r.CompileShadersForDevelopment被设置,COMPILE_SHADERS_FOR_DEVELOPMENT也会是true。
[*]#if TRANSLUCENT_SELF_SHADOWING。当对象以FSelfShadowedTranslucencyPolicy渲染时,它将会被定义。这个是为Lit Translucency support服务的。
[*]#if SIMPLE_FORWARD_DIRECTIONAL_LIGHT和 #if SIMPLE_FORWARD_SHADING。当Light Map为stationary directional light渲染时,他们被设置。
[*]#if FORWARD_SHADING。当r.ForwardShading被设置成1时,它被设置。
Review
我们已经学习过如何通过改变一些项目设置,来减少shader permutation总数量,从而达到加快重新编译的目标;通过使用recompileshader console命令,就可以避免所有shader都被重新编译,尽管下次editor启动时仍然需要全部重新编译;另外,我们也学习了在什么条件下,一些common proprecssor定义被设置,这也帮助我们了解哪些代码是跟我们目标相关的。
Next Post
在下一文章中,我们将介绍如何修改已有的shader pipeline,添加新的shading model,生成toon-like效果。文章中的技巧来自于FlixK’s 开发博客。 翻译别人的文章,开头应该写明白吧? 接受您的批评,已经改正,请原谅我的草率,这几篇文章翻译人很多,我原来只是想提高自己而翻译的,想不到给您带来困恼。 不管是目的是啥,不是原创摆明出处才是正确的创作态度
页:
[1]