39

游戏性能优化杂谈(十)

 3 years ago
source link: https://zhuanlan.zhihu.com/p/272840199
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

GPU管线的最前端通常是一个指令获取与译码单元。这个单元从内存取出CPU传递过来的指令,将其进行译码。一些命令发送给后面的微核心进行执行,比如DMA数据传输、和CPU之间的通信同步等等;另外一些命令则对GPU内的数百个状态寄存器进行设置,相当于配置流水线上各个环节的参数。

这些参数往往并非是直接配置到流水线作业当中的实际硬件上,而是作为一个寄存器文件保存在内部。这样的寄存器文件会有多个,从而可以保存多套不同的流水线配置。

想象一下你有一个食品加工厂,现在流水上正在生产山楂糕。这时有一个新的订单过来,需要你生产怪味花生。如果你这个时候就立即改生产线,则必然会影响还呆在流水线上的山楂糕半成品。所以,你往往会做的事情是将生产怪味花生的参数录入一个配置文件当中,然后放在那里,等待山楂糕生产完毕,就读入这个配置文件开始生产怪味花生。

这个接受订单并且录入生产所需配置信息的,就是GPU最前端的ME(Micro Engine)

ME在性能上主要需要注意的问题有两点:

  1. 一些可能会长时间阻塞ME的命令。比如DMA命令。如果需要DMA的数据很大,比如Unity当中的Grab PASS,这个PASS使用DMA将整个渲染结果拷贝回CPU可读的空间,因为这个DMA发生在ME,那么就会长时间阻塞命令队列,也就是堵在了下水道入口,后面的下水道管道当中的残水流光之后,虽然处于无水状态,但是水池里的水也进不来。还有一种命令就是同步命令,这种命令往往是必要的,比如用于与CPU的同步,或者是等待GPU上一批的处理,等等。但是,由于GPU的管线有一定的长度,是否一定需要在入口处等待,还是说可以在中间或者出口的某个地方等待,从而保持管线里面有水,这是值得思考的。
  2. 寄存器文件的大小。因为ME会将不同的管线配置写入不同的配置文件(术语称为context),而这个配置文件因为是一种GPU片上存储,所以容量十分有限。通常,GPU同时可以支持的配置文件总数也就是几个,这样的数量级。所以,如果命令队列当中频繁切换管线配置(属于称为context roll),那么当配置文件用完之后,ME将暂时无法继续执行命令队列,进入等待状态,直到ME之后的管线部分完成排出一些工作量,释放context为止。打个比喻的话,也就是如果把来伊份的某个店头的零售终端直接接在工厂的生产订单系统上的话,那么就会出现好几十种,每种一两个这种订单。在这种情况下,即便生产线采用了某种外星科技可以瞬间切换配置,但是如果生产线一次只能记住10个配置,那么还是会遇到需要频繁停止重新输入配置参数的问题。而且,产线的切换需要等待产线上所有货物出来。现在一种货物就1、2包,整个长长的产线空空如也,只能干巴巴看着这2包从头跑到尾。现代汽车制造业可能每几分钟就能有一台车下产线,但是这并不意味着一台车只在产线上呆几分钟。将使用相同配置的生产对象合并,尽量填充满产线,是关键。

当产线配置好之后,需要准备待加工的生料,也就是原材料。GPU当中会有专门的硬件模块负责从内存读取顶点信息。读取顶点信息之后,会有硬件模块将这些顶点按照一定尺寸进行分组,然后以组为单位送入管线。

这就好比我们的食品加工产线上,每次提取原材料一定是有一个固定的量:比如每次提取1公斤山楂,或者花生。这1公斤(1份)作为后面加工环节的一个基本单位,会被同时处理。目的就是通过增加带宽换取高性能。

接下来GPU就会利用其强大的并行计算能力,对同一组当中的数据,用同样的代码进行并行处理。目前我们的数据是顶点,GPU接下来就是执行我们所提供的VS shader,也就是顶点着色器程序,将以object space记录的各个模型顶点坐标,转换成场景坐标,也就是将模型放入场景当中。

当这步完成之后,我们得到了一系列场景坐标当中的顶点。接下来是一个被称为PA(Primitive Assembler)的机构,将这些顶点按照index buffer当中的index的顺序,组装成为三角形。然后送入光栅化模块(rasterizer)。

在这个读取顶点、进行坐标变换(VS计算)、并且组装三角形的过程当中,可能出现的性能瓶颈有3处:

  1. GPU读取顶点数据、索引数据的性能。因为这本质上也是内存访问,所以其受到内存访问带宽的制约。这个制约具体是多少,取决于GPU当中负责该功能的模块与内存的连接方式,以及该模块自身工作方式,不同的GPU型号不同。但是,有一点是共通的,那就是这个性能的单位是(字节/时钟周期),而不是(顶点/时钟周期)或者(索引/时钟周期)。这就是说,顶点和索引数据的存储格式非常重要。一个16bit的索引,相比较32bit的索引,在这里速度会快一倍。
  2. VS程序自身的复杂度,特别是是否含有内存访问指令。通常来说,相对于内存读写,计算是非常快的。所以在一定范围内,VS程序不会是瓶颈。但是,如果VS程序当中包括内存访问,比如访问地形数据,或者动画数据,或者别的什么,那么情况就很可能反转,VS自身的执行时间成为这个阶段的瓶颈。
  3. VS执行结果的保存。VS输出的结果是中间结果,所以通常并不会回写到内存(当然,当代GPU已经有能力通过特殊指令回写),而是存储到片上空间。这个片上空间通常容量也十分有限,所以如果VS为每个顶点输出的属性很多,那么这个片上空间所能容纳的顶点数也就减少,当其满了的时候,VS将进入等待,直到GPU当中的后续环节消耗掉这些内容的一部分,释放出足够空间。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK