Adreno GPU上的DirectX平台优化 (4)

Adreno GPU上的DirectX平台优化 (4)

5.3 Adreno GPU性能建议

除了上面提供的基于贴图的渲染改进建议,在尝试利用Adreno GPU时,还有其他重要的性能考虑。本节接下来针对Adreno gpu的Direct3D11.1应用程序的性能提供一系列可行的建议。

5.3.1 编写高效着色器代码

为了充分利用Adreno着色器资源,了解一些架构方面的内容是很重要的。

这里有一些技巧可以让你的着色器表现得更好。

  • 着色器编译是昂贵的

当应用程序使用着色器时,高通的优化编译器在编译期间牺牲了CPU处理时间,以换取更快的执行时间。着色器应该只在非关键时刻创建(例如,应用程序创建,加载屏幕,显示简单菜单)。

  • 使用内嵌函数

作为HLSL语言内嵌函数的一部分应该在可行的情况下使用,因为您不需要通过编写自己的函数来重新发明轮子,而且它们很有可能针对特定的着色器配置文件进行优化。请参考HLSL内部函数列表,了解所有支持的函数:http://msdn.microsoft.com/en-us/library/windows/desktop/ff471376(v=vs.85).aspx

  • 使用适当的数据类型

通过在代码中使用适当的数据类型,编译器和驱动程序中的优化器可以优化代码和着色指令。例如,使用float4数据类型而不是float数据类型会阻止编译器以一种可以在硬件上共同发布输出的方式来安排输出。细小的错误有时会对性能产生很大的影响。例如,下面的代码应该使用一个指令槽。

int4 ResultOfA(int4 a) 
{ 
 return a + 1; 
}

而下面的代码可能会因为1.0而消耗8个指令槽。

int4 ResultOfA (int4 a) 
{ 
 return a + 1.0; 
} 

变量a将被转换为float4,然后加法将在浮点数中完成,最后结果将被转换回返回类型int4。

  • 封装标量常数

将标量常数封装到由四个通道组成的向量中,从根本上和整体上提高了硬件的获取效率。例如,在动画系统中,填充增加了可用骨骼的数量。下面的代码展示了一种简单的实现方法:

float scale, bias;
float4 a = Pos * scale + bias;

下面的代码可能少使用一条指令,因为编译器可以优化这一行,使之成为更有效的指令。

float2 scaleNbias;
float4 a = Pos * scaleNbias.x + scaleNbias.y;
  • 2 x2像素处理

扫描转换器“粗略扫描”每个三角形,如图5-6所示排列为8x8块

图5-6扫描转换器“粗略扫描”每个三角形

在此之后,每个贴图被分割成对齐的2x2四边形。其中一些四边形可能包含"死"/"无效"像素,如图中红色所示

图5-6。非常小的(小于2x2像素)三角形会浪费至少60%的GPU处理能力。考虑使用只使用较大三角形的几何LODs。例如,当执行一个粒子系统时,如果使用更小的三角形,效率就会降低。

  • 纹理采样

事实上,硬件可以同时在2x2像素上工作,这在像素层面上是个挑战。这些四边形中的四个或更多被分组成一个像素向量(又叫像素线程)。优化网格,使像素向量很好地组合在一起,从而更有效地利用纹理缓存。

其他避免纹理停滞的策略有:

  • 避免随机访问
  • 避免3D体积纹理
  • 避免在一个像素着色器中获取7个纹理;更合适的数字是4
  • 使用压缩格式:更好的内存使用,更少的停顿
  • 尽可能使用mipmap

一般来说,三线性和各向异性滤波比双线性滤波昂贵得多,而点滤波和双线性滤波没有区别。

纹理滤波会影响纹理采样的速度。32位格式的2D纹理中的双线性纹理查找需要花费一个周期。加上三阶线性滤波,成本增加一倍,达到两个周期。Mipmap可以将此降低为双线性成本,因此在现实情况下平均成本可能更低。添加各向异性滤波与各向异性度相乘。这意味着16x各向异性查找比常规各向同性查找慢16倍。因为各向异性过滤是自适应的,所以这种情况只发生在需要各向异性过滤的像素上,这些像素可能最终只有几个像素。现实世界的经验法则是,各向异性过滤的平均成本不到两倍。

不同的纹理格式对纹理采样性能影响很大。32位纹理需要1个周期来获取。所以所有32位和更小的格式(包括所有压缩格式)都是单周期的。64位格式需要2个周期,而128位格式需要4个周期。

Cube maps和投影纹理查找不会产生任何额外的成本,而指定着色器梯度基于ddx() / ddy()-将增加一个额外的周期成本。这意味着常规的双线性查找的一个常规周期需要两个指定着色器的梯度。请注意,这些指定着色器的梯度不能在查找过程中存储,所以如果你在同一个采样器中再次使用相同的梯度进行纹理查找,它将再次消耗一次循环命中。

  • 3D 纹理

使用3D纹理会严重影响内存和缓存的使用。有效利用纹理内存已经成为实时3D内容的主要任务。向纹理映射添加额外维度会显著增加其内存占用。有四种主要的技术可以帮助提高3D纹理映射的性能:

* 保持叫较小的纹理(每个方向 < 32 texels)
* 在适当的地方通过镜像反复应用。体积细节纹理可以重复到很大的效果。使用镜像地址(D3D11_TEXTURE_ADDRESS_MIRROR)模式来对称纹理,比如	体光贴图。镜像(D3D11_TEXTURE_ADDRESS_MIRROR_ONCE)寻址模式对于3D灯光映射特别有用。
* 尽量保持纹理位深度较低。体积细节纹理和光线贴图通常是灰度的。对于这些,使用单通道纹理。
* 使用压缩。开发者应该为他们的应用做出适当的大小/质量权衡。
  • 分支

静态分支的性能很好,但是要冒额外GPRs的风险。在着色器中使用动态分支具有特定的、非恒定的开销,这取决于确切的着色器代码。因此,使用动态分支并不总是性能上的优势。因为多个像素是作为一个被处理的,在一些像素进入一个分支而另一些不进入的情况下,GPU必须执行两条路径(所有的指令都执行,但是有一个屏蔽位控制输出,所以只有适当的像素会受到影响)。在这种情况下,性能会比单独处理每个像素时表现更差。如果所有像素都采用相同的路径,那么GPU就能够真正采取分支,这对性能有好处

  • 封装着色器插入器

着色器插值的值(在顶点着色器和像素着色器之间传递的值)需要GPR来保存输入到像素着色器中的数据。因此,您应该尽量减少使用它们。在值一致的地方使用常量。所有的插值器都有四个组件,不管你是否使用它们,所以把值打包在一起。将两个float2纹理坐标放到一个float4值中是一种常见的做法,但其他策略则使用更有创意的打包。

  • 着色器的细节程度

为了提高着色器的效率,类似于几何层次的细节,着色器层次的细节可以实现。基于与相机的距离,着色器的质量、成本和能源效率进行了交换。物体离相机越远,成本和质量就越便宜,同时能源效率也会提高。

LOD系统的粒度可以基于每帧或每对象。LOD值算法会随着距离相机的距离而变化。对于照明系统,LOD级别可以是:

  1. 法线映射像素照明附加的详细地图
  2. 没有详细贴图的法线贴图逐像素光照
  3. 顶点照明与一个颜色纹理
  • 最小化着色器GPRs

最小化GPRs是最重要的性能优化方法。向编译器输入更简单的着色器有助于保证最佳结果。有时,修改HLSL以保存哪怕是一条指令也可以保存GPR。不展开循环也可以保存GPRs,但这取决于着色器编译器。(相反地,展开的循环倾向于将纹理获取简单地集中到着色器的顶部,从而导致需要更多的GPRs来同时保存多个纹理坐标和获取结果。)

  • 避免在着色常量上进行数学运算

自从着色器问世以来,几乎每一款游戏都在着色器常量上花费了大量的指令。在你自己的着色器中识别这些指令,并将这些计算转移到CPU。在编译后的微代码中,在着色器常量上识别数学可能更容易。

  • 避免Uber-Shaders

uber-shaders将多个着色器组合成一个使用静态分支的着色器。如果您试图减少状态更改和批量绘制调用,那么使用它们是很有意义的。然而,这通常以增加GPR计数为代价,这将对性能产生影响。

  • 避免在像素着色器中杀死像素

一些开发人员认为手动杀死像素着色器中的像素(HLSL中的clip())可以提高性能。这些规则并不简单,原因有二:

* 如果线程中的某些像素被杀死,并且其他的没有,则着色器仍然执行。
* 如果使用剪辑,则驾驶员必须禁用Early-Z。 原因是因为如果启用Early-Z并且在像素着色器中终止像素,则深度缓冲器值对于该像素不正确地更新。 因此,驱动程序必须在着色器使用剪辑()函数时禁用Early-Z。

建议只在像素着色器中使用 clip() 指令来实现像 alpha 测试这样的效果是绝对必要的。 否则,不要使用 clip() 作为性能优化,因为它可能会导致性能下降。

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值