上一篇探讨了G-Buffer里如何紧凑并高质量地存储normal。长期以来,deferred一直被认为只能用于高端显卡,低端卡由于功能和带宽的限制,不适合使用deferred。虽然现今移动设备大行其道,但所有移动设备和桌面相比,都只能算低端。所以OpenGL ES上也一直都被forward渲染占领。一个引擎维护两套流水线是非常麻烦的事情,尤其对于KlayGE这样的开源轻量引擎。所以,如果能在低端设备上也能用同一条deferred流水线,能给维护和扩展提供巨大的方便。
经过一定的改进,deferred流水线已经可以在中上等级的OpenGL ES 2设备和D3D11 feature level 9.3设备上跑了。这样的硬件涵盖了NV GeforceFX 6000以上(2004年)、ATI Radeon X1000以上(2005年)、Intel GMA X3000以上(2006年)、Imagination PowerVR 535以上(2007年)、NV Tegra 2(2010年)、ARM Mali-400(2011年)、Qualcomm Adreno 203以上(2012年)的设备。
Deferred的障碍
在低端硬件上执行deferred存在许多障碍。下面我会逐条讨论如何客服它们。
带宽
最大的障碍毫无疑问来自于带宽。对于桌面硬件情况还好,但对移动设备来说,即便是目前市面上带宽最高GPU,Tegra K1和PowerVR SGX554 MP4,也只有17.1GB/s。比2003年的GeForce FX 5900还要低40%。所以节约带宽变得更加危急。
带宽问题的解决方案,甚至有可能是唯一的解决方案,是TBDR。低端设备上可以用基于PS的TBDR,但由于uniform变量数量的限制,每个batch只能4个光源。即便如此,也省了4倍的带宽。
SIGGRAPH 2013上ARM有个talk叫Challenges with High Quality Mobile Graphics。里面提到了如何利用tile based硬件实现on-chip deferred,让IO只发生在chip之内,可以认为完全解决了带宽问题。
功能
除了带宽之外,低端硬件的功能也比中高端少。所以有必要找一些替代的做法,以补偿功能缺失带来的麻烦。
MRT在很多硬件上不提供。这个问题很早以前就通过多次渲染解决了。虽然在生成G-Buffer的时候需要多渲一遍,但后面流水线都简单了。如果带宽是瓶颈,渲两遍的代价只会比MRT略高而已。
深度纹理大部分硬件都有,但Tegra 2和3都没有。在KlayGE 4.4里,大部分需要用到的深度纹理都用打包到RGBA8的方式来解决了。这个版本又多了一个扩展。因为tile based方法都需要在每个tile上存两个深度,最近的和最远的。所以需要把两个float转成half,分别打包到RG和BA。
浮点纹理在4.4里也解决了大部分,4.5里把剩下的也做了同样的处理。能打包的就打包,其他的用8bit通道来近似。虽然在那种情况下失去了HDR,但流水线能顺利进行下去。
带梯度、lod和bias的纹理采样指令在不同硬件上支持程度也不同。这里需要通过一些宏把它们分开。虽然麻烦,但能支持各种硬件的情况。
指令长度和寄存器数量是个恼人的问题,比如feature level 9.3的shader model 2.x只允许96条指令和32个常量寄存器。经过一系列的优化,减少指令条数,把一些uniform变量合并在一起,终于能把TBDR塞进低端硬件的要求中。
在解决了带宽和功能问题之后,TBDR可以顺利地在GLES2和9.3设备上执行了。速度高于传统DR和FR,也可以轻松支持上百光源。
进一步降低的可能
这样的硬件配置涵盖了绝大部分市面上的硬件,但确实仍有一批达不到这个要求的硬件存在。比如Tegra 3在WinRT平台上只支持feature level 9.1。在这样的平台上遇到的一个问题是,纹理依赖读取的条数非常有限甚至没有。对于TBDR来说,需要读取tile信息,并根据它来做后面的操作。这样的shader会编译失败。目前还没有仔细去解决它,留给以后的版本吧。
本篇讲了如何把deferred框架扩展到低端硬件。下一篇会介绍如何支持Oculus Rift显示。
Comments