【GPU Gems】Chapter 28. Graphics Pipeline Performance

今天分享的是GPU Gems上关于性能优化的文章,这里是原文地址。

1. 概览

随着硬件的升级,图形渲染管线也变得越来越复杂,使得渲染瓶颈的定位也变得复杂起来。

1.1 渲染管线

图形渲染管线从大的方面可以分成CPU跟GPU两部分,这里的主要关注点在GPU上面。下面这张图给出了GPU渲染管线并行执行的且容易导致瓶颈的几个主要的功能单元:

GPU渲染管线

1.2 优化方法

之所以要定位瓶颈,其最终的目的是为了优化应用的渲染表现,使之用最小的消耗得到最优的表现,而优化工作总的来说可以分成如下三步:

  1. 定位瓶颈
  2. 优化瓶颈
  3. 重复上述两步

2. 瓶颈定位

瓶颈流程图

上图给出了瓶颈定位的主要流程图,这里的定位是从(图形渲染管线顺序)后往前进行的,这里需要注意的是,不同的物体其瓶颈可能是不一样的(比如一个天空盒的主要瓶颈可能在PS上,而一个蒙皮物体的瓶颈可能在VS或者CPU上)。

这里定位瓶颈的方法是通过对每个阶段,调整其负载,观察性能表现是否有变化而得到的。

2.1 Raster Operations(RO)

图形渲染最后一个阶段为RO阶段,在这个阶段会完成depth/stencil的读写与test,color的读写,alpha blend/test等工作,从这些描述可以看出,这个阶段对于frame-buffer的带宽有着比较强的依赖。

定位frame-buffer带宽瓶颈的最好的方法就是调整color/depth buffer的格式,如果格式精度下降导致性能上升,说明就是这个地方出了问题。

需要说明的是frame-buffer带宽是GPU memory clock的一个函数,因此修改memory clock也可以用来定位瓶颈。

2.2 Texture Bandwidth

贴图带宽可以理解为读取贴图数据所需要的时间,出于对功耗的考虑,移动端的芯片硬件尺寸注定不能过大,否则功耗过高,掉电快发热高,十分影响体验,而对功耗影响最显著的一项因素就是带宽,这也就注定了移动端的带宽占用不能过高(Arm在Unity Forum 2019上的建议是,移动端在CPU的带宽占用不能超过2GB/s),因此也就导致了对带宽依赖较强的一些环节,比如贴图读取等在处理速度上会受到限制,虽然当代GPU增加了贴图缓存来降低贴图读取的延迟,但是延迟依然还比较明显。

定位这个问题当然可以通过修改贴图格式来判断,不过这种做法比较麻烦,更常用的做法为通过mipmap bias降低读取的贴图的mip层级,采取更低分辨率的贴图来看看性能是否提升。

贴图带宽同样也是GPU memory clock的函数(这里能通过memory clock来判断瓶颈吗?那不就跟前面的冲突了,导致无法识别?)。

2.3 Fragment Shading

这个阶段主要对应的是fragment着色计算的消耗。通常fragment shading+frame-buffer带宽对应的是GPU的一项非常重要的参数:fill-rate。虽然这两项都可以通过降低渲染分辨率来提升性能,但是他们两项本质上是不同的。

固定管线中基本上不会在这个地方产生瓶颈,但是随着可编程管线的开放与优化,现在这个地方成为瓶颈的概率也在增加。

定位fragment shading瓶颈的第一个方法是降低渲染分辨率,因为之前已经分析过不是frame-buffer带宽的瓶颈,因此如果因此出现了性能提升,那就肯定是这个地方的问题。另一个方法则是修正fragment shader的长度,这里注意不要添加一些会被编译器优化的代码。

Fragment shading是GPU core clock的函数。

2.4 Vertex Processing

在这个阶段会完成顶点相关的一些运算,包括坐标变换,法线转换等,经过这个处理后,会输出与顶点相关的若干属性,后面会用作clipping与光栅化输入。

定位VS瓶颈的方法是修改VS的长度,VS处理的速度同样也是GPU core clock的函数。

2.5 Vertex & Index Transfer

在渲染管线开始前,GPU需要从某个地方将顶点与索引数据读取出来,这就是这个阶段所完成的功能。

这个阶段的性能表现取决于GPU是从哪个地方获取这些数据的,比如最开始是直接从系统主存中读取,而后面出于优化考虑,将数据提前传输到了local frame-buffer memory中。

判断这个阶段是否会成为瓶颈就是直接修改顶点格式,减少每个顶点传输的数据长度。

如果上述的所有过程都没有导致性能上升,那么问题就是出在CPU上面了。在定位到瓶颈之后,下一步就是优化过程。

3. 优化

根据不同的瓶颈,这里的优化方法也有所不同。

3.1 CPU瓶颈

CPU瓶颈主要有如下两种优化方法:

  1. 降低资源Lock操作。避免CPU/GPU的频繁传输
  2. 增加合批的力度,避免CPU提交导致的高消耗

3.2 Vertex Transfer

顶点、索引传输阶段的额瓶颈有如下几种方法:

  1. 减少顶点属性
  2. 将部分属性放到VS中直接计算
  3. 降低索引位数
  4. 按照顺序来对顶点进行访问(调整顶点顺序,增加缓存命中率)

3.3 VS优化

VS阶段的优化有如下几种思路:

  1. 优化顶点访问的顺序,增强缓存命中率
  2. 减少物件的定点数量
  3. 使用Mesh LOD
  4. 将一些低频计算放到CPU
  5. 使用正确的坐标空间,减少无谓计算
  6. 使用VS动态分支,减少代码计算的浪费

3.4 Fragment Shading优化

PS优化有如下几条思路:

  1. 先单独绘制一遍深度
  2. 使用early-z的功能降低浪费
  3. 将一些复杂计算的结果离线算好存储到贴图中
  4. 将一些低频计算放到VS中
  5. 降低格式精度
  6. 避免不需要的normalization
  7. 针对不同的物体使用不同计算复杂度的PS
  8. 减少三线性采样
  9. 使用尽可能简单的shader格式

3.5 贴图带宽优化

贴图带宽优化有如下几种方法

  1. 缩小贴图尺寸
  2. 选择合适的压缩方法
  3. 尽可能的避免大尺寸高精度的贴图
  4. 使用mipmaping

3.6 Frame-buffer带宽优化

FB带宽优化有如下几条方法:

  1. 先绘制一遍depth
  2. 避免alpha-blending带来的overdraw
  3. 避免无所谓的depth writes
  4. 避免不必要的color clear
  5. 在CPU侧做一下粗糙排序,按照从前往后的顺序进行绘制
  6. 优化天空盒渲染方案
  7. 不必要的情况不要使用浮点格式的RT
  8. 使用尽可能低精度的depth与color

参考文献

[1]. Chapter 28. Graphics Pipeline Performance

你可能感兴趣的:(【GPU Gems】Chapter 28. Graphics Pipeline Performance)