GPU的光栅化模块之后,是一个工作量打包模块。这个模块负责将光栅化的结果(fragment)按照一定大小(N卡:warp32,A卡:wavefront64或wavefront32)打包,然后交给下面的处理阶段(PS)
这里需要注意的一个细节是,离散化的粒度虽然最终是像素级,但是在近代GPU当中,离散化模块输出的单位却不是单个像素,而是Quad(2x2像素)。其中的一个原因(可能并不是最根本的那个原因)是单个像素无法计算ddx、ddy,从而在PS当中判断选用贴图的MIPS层级会发生困难。当然,我也怀疑这可能与GPU当中贴图单元与计算单元配比等有关,亦或是和内部带宽有关,但是无从验证。
总之,对于当前主流的GPU来说,一个三角形至少会产生一个Quad。当三角形很小面积无法覆盖一个Quad的时候,未被覆盖的那些像素(准确来说是fragment)会被标志为无效,但是坑并不会让出来。
所以对于上一篇所举的那个dither的例子,一些位置上整个Quad被mask掉,而另外一些地方其实只是Quad里面的部分像素被标志为无效。
什么意思呢?就是其实PS工作量总的来说是减少了,但是并没有减少很多。如果用GPU抓帧工具来看,会发现很多warp/wavefront里面的thread不饱满,有的甚至里面只有几个thread。
就好像公司组织去旅游,包了6辆旅游巴士,结果当天1半人取消。本来呢重新安排一下位置可以取消3辆车,但是人与人之间各种亲疏远近各种嫌弃,或者平级领导不愿同乘还得跟班陪坐什么的,最终可能只能取消2辆甚至1辆都不行。
在这种情况下,很多被抠掉的像素其实依然在管线内参与整个PS计算,只不过计算结果被丢弃而已。
更为不幸的是,这种丢弃,因为GPU和内存之间有cache,也就是有cache line的存在,很多时候对于内存带宽的节省,也是打折扣的。cache line与内存交换一次就是连续64字节,按照32bit/pixel计算,就是16个像素(4x4),也就是4个Quad(2x2)。这些都是定死的,所谓的丢弃,只不过是原样读入原样写出,带宽消耗还是那么多。
即便是Quad单位的丢弃,依然是有限制的。光栅化模块,虽然很快,但是产生Quad的速率是一定的。后续的打包模块,将8个或者16个Quad打一起那个,也不是拥有无限耐心的。什么意思呢?就是好比公共汽车,它是有运营时刻表的。并非是包车,坐满再开。所以如果大量Quad被干掉,要么一个都没有公交有时这个班次也就取消了,否则1个人都开。
为什么?因为司机并不知道后面还有多少人,还要多久来。再说,既然是定线定时运营,错过了就坐下一班,这是基本。
来源:oschina
链接:https://my.oschina.net/u/4274700/blog/4716962