Shader整洁之道

作者:追风剑情 发布于:2016-8-31 22:20 分类:Shader

Cg/HLSL中3种精度的数值类型

类型 精度
float 最高精度的浮点值。通常使用32位来存储
half 中等精度的浮点值。通常使用16位来存储,精度范围是-60000~+60000
fixed 最低精度的浮点值。通常使用11位来存储,精度范围是-2.0~+2.0

     上面的精度范围并不是绝对正确的,尤其是在不同平台和GPU上,它们实际的精度可能和上面给出的范围不一致。通常来讲。
  • 大多数现代的桌面GPU会把所有计算都按最高的浮点精度进行计算,也就是说,float、half、fixed在这些平台上实际是等价的。这意味着,我们在PC上很难看出因为half和fixed精度而带来的不同。
  • 但在移动平台的GPU上,它们的确会有不同的精度范围,而且不同精度的浮点值的运算速度也会有所差异。因此,我们应该确保在真正的移动平台上验证我们的Shader。
  • fixed精度实际上只在一些较旧的移动平台上有用,在大多数现代的GPU上,它们内部把fixed和half当成同等精度来对待。

      尽管有上面的不同,但一个基本建议是,尽可能使用精度较低的类型,因为这可以优化Shader的性能,这一点在移动平台上尤其重要。从它们大体的值域范围来看,我们可以使用fixed类型来存储颜色和单位矢量,如果要存储更大范围的数据可以选择half类型,最差情况下再选择使用float。如果我们的目标平台是移动平台,一定要确保在真实的手机上测试我们的Shader,这一点非常重要。

规范语法

      DirectX平台对Shader的语义有更加严格的要求。这意味着,如果我们要发布到DirectX平台上就需要使用更严格的语法。例如,使用和变量类型相匹配的参数数目来对变量进行初始化。

避免不必要的计算

如果我们毫无节制地在Shader(尤其是片元着色器)中进行了大量计算,那么我们可能很快就会收到Uity的错误提示:
temporary register limit of 8 exceeded

Arithmetic instruction limit of 64 exceeded; 65 arithmetic instructions needed to compile program
出现这些错误信息大多是因为我们在Shader中进行了过多的运算,使得需要的临时寄存器数目或指令数目超过了当前可支持的数目。不同的Shader Target、不同的着色器阶段,我们可使用的临时寄存器和指令数目都是不同的。
通常,我们可以通过指定更高等级的Shader Target来消除这些错误。

Unity支持的Shader Target

指令 描述
#pragma target 2.0 默认的Shader Target等级。相当于Direct3D 9上的Shader Model 2.0,不支持对顶点纹理的采样,不支持显式的LOD纹理采样等
#pragma target 3.0
相当于Direct3D 9上的Shader Model 3.0,支持对顶点纹理的采样等
#pragma target 4.0
相当于Direct3D 10上的Shader Model 4.0,支持几何着色器等
#pragma target 5.0
相当于Direct3D 11上的Shader Model 5.0

需要注意的是,由于Unity版本的不同,Unity支持的Shader Target种类也不同。

什么是Shader Model呢?

      Shader Model是由微软提出的一套规范,通俗地理解就是它们决定了Shader中各个特性(feature)的能力(capability)。这些特性和能力体现在Shader能使用的运算指令数目、寄存器个数等各个方面。Shader Model等级越高,Shader的能力就越大。

虽然更高等级的Shader Target可以让我们使用更多的临时寄存器和运算指令,但一个更好的方法是尽可能减少Shader中的运算,或者通过预计算的方式来提供更多的数据。

慎用分支和循环语句

      在最开始,GPU是不支持在顶点着色器和片元着色器中使用流程控制语句的。随着GPU的发展,我们现在已经可以使用if-else、for和while这种流程控制指令了。但是,它们在GPU上的实现和CPU上有很大的不同。大体来说,GPU使用了不同于CPU的技术来实现分支语句,在最坏的情况下,我们花在一个分支语句的时间相当于运行了所有分支语句的时间。因此,我们不鼓励在Shader中使用流程控制语句,因为它们会降低GPU的并行处理操作(尽管在现代的GPU上已经有了改进)。

      如果我们在Shader中使用了大量的流程控制语句,那么这个Shader的性能可能会成倍下降。一个解决方法是,我们应该尽量把计算向流水线上端移动,例如把放在片元着色器中的计算放到顶点着色器中,或者直接在CPU中进行预计算,再把结果传递给Shader。当然,有时我们不可避免地要使用分支语句来进行运算,那么一些建议是:

  • 分支判断语句中使用的条件变量最好是常数,即在Shader运行过程中不会发生变化;
  • 每个分支中包含的操作指令数尽可能少;
  • 分支的嵌套层数尽可能少。

不要除以0

标签: Shader

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号