移动平台延迟渲染

写主机世代演进的时候提到过延迟渲染。这次就给同学们讲一下移动平台延迟渲染的实现,并利用openGL扩展,充分优化延迟渲染的几个性能瓶颈。如果是有意从事引擎开发、TA的同学请一定把这些概念弄清。
回顾openGL ES 3.0
要说openGL ES 3.0相比openGL ES 2.0的最重大进步之处,我个人认为有如下几个:
VAO/UBO
Instanced Rendering
Tranform feedback
Multiple Render Target
当然SSO,Immutable Texture Storage,half-float color,VTF等特性也都很重要
而在这些新特性之中,MRT可以说是最重要的特性,因为它可以完全改变渲染的方式,产生新的可能性。而其他API一般是性能提升/用法改进,没有MRT的革命性。
Multiple Render Targets
MRT,可以在一个drawCall向多张贴图进行渲染(至少为4),MRT最主要的用途,就是实现各种上级渲染,比如Deferred Shading,forward+ Shading,Deferred+ Shading。
延迟渲染一般分三个阶段:
Geometry State:几何阶段收集场景的color,normal,depth,albedo信息
Lighting Stage:估计每个像素影响的光照信息
Shading State:依靠光源信息来计算场景最终的颜色

几何阶段代码实现


延迟渲染的优缺点:
优点:
能同时处理众多光源
提供稳定的帧率
缺点:
消耗大量的显存和带宽
光照阶段所有材质必须使用完全相同的光照模型
不能很好的支持半透明
不支持硬件抗锯齿
优化:使用Framebuffer Fetch
延迟渲染在光照处理阶段,要对几何阶段产生的4个RT进行采样,需要消耗大量的带宽。可以使用 Framebuffer Fetch
扩展来减少采样时的带宽消耗。
EXT_shader_framebuffer_fetch
语法:

使用 Framebuffer Fetch
后渲染流程简化为入下图所示。

优化++:使用Shader Pixel Local Storage
有了Framebuffer Fetch虽然可以减小硬件采样时的效率损失,但是还是要消耗大量的显存和带宽,优化要优化瓶颈,有没有处理这个问题的方法。当然有,就是 Shader Pixel Local Storage
。
Shader Pixel Local Storage 是一个可以完全扭转MRT所有IO消耗的扩展。可以说,是一个神扩展。它的原理很简单,就是将数据存在GPU的片上缓存而不写入主存(显存)。当然原理听着简单,实现起来可并不容易。因为缓存很小也很贵。不过,得益于几乎所有主流的移动 GPU 都是 Tile based,以瓦片为单位存储消耗就很小了,在缓存上也完全存的下。

主流桌面GPU没有 Tile based,故桌面 GPU 也没有支持 Shader Pixel Local Storage 的,可惜。F+这种瓦片渲染普遍基于计算着色器实现,十分精细
要使用Shader Pixel Local Storage,首先开启扩展
#extension GL_EXT_shader_pixel_local_storage : enable
之后使用 Pixel Local Storage 存储结构代替MRT即可, Pixel Local Storage 存储结构和 shader io block 很接近,但是需要描述Qualifier和layout:

实例:



Pixel Local Storage
Pixel Local Storage优化的延迟渲染完整shader实例代码(使用最基础的diffuse光照模型)
几何处理阶段

估算光源

最终着色(resolve everything)

Shader Pixel Local Storage和MRT性能比较:


小结
本文标题是延迟渲染,所以实现的都是延迟渲染。依靠扩展实现延迟渲染,其实没那么费(但是半透明以及MSAA的问题依旧存在)。其实依靠移动平台的这些优势做F+优势更明显。到此,其实也能说明一个老生常谈的问题,那就是 openGL 其实没那么差。
metal作为openGL的精神继承人,在继承AMD mantle API高效的同时,实现得比openGL还要简单,还吸收了MSAA depth/stencil resolve,framebuffer fetch, shader local storage等openGL扩展作为标准的一部分,充分说明了APPLE务实的作风。反观vulkan的设计,太学院派了,非常复杂不说,甚至相比openGL本身还出现了功能上的缺失真的不知道怎么评价。
APPLE metal设计之初的核心思路是让buffer格式被CPU和GPU同时支持,来处理openGL buffer在内存和显存之间的反复拷贝的问题,可以说一开始是很面向移动设备的。而AMD mantle最初的设计核心是让Pipeline state的状态一致可预测,减少Pipeline状态切换和状态检查的性能损失。补充一下,免得误导。
参考文献
Advances in OpenGL ES 3.0 - Apple iOS7 Tech Talks 2013
Deferred Rendering Techniques on Mobile Devices - Ashley Vaughan Smith[GPU pro 5]
Bandwidth Efficient Graphics with ARM® Mali™ GPUs - Marius Bjørge[GPU pro 5]