记录一下对 mobile GPU 基于区块渲染的工作方式的一些研究

1. 传统GPU管线

foreach( primitive ) 
    foreach( fragment ) 
        render fragment 

IMR

       全屏只拥有一个全尺寸的工作集。只要消耗集中在任何片段都需要访问全工作集带来的巨大的带宽压力

2. mali TBDR

foreach( tile )  
    foreach( primitive in tile ) 
            foreach( fragment in primitive in tile )  
                render fragment  

TBR

       目标:减少渲染期间所需的功耗巨大的外部内存访问

       不再以primitive为单位,执行完顶点着色器后立刻执行片段着色器。而是以rendertarget为单位,在一个rendertarget所有的顶点着色器执行完毕后,再执行片段着色器。执行顶点着色器的过程中,GPU将屏幕分割为16x16的区块,将订单数据分散发送到每个工作集。片段着色器一次只处理一个区块,计算完几个区块之后才访问下一个区块。

优点:

  1. 工作集的所有访问都属于本地访问,速度快、功耗低(传统模式需要占用的存储巨大导致只能放于内存之中)
  2. 区块足够小,我们实际上可以在区块内存中本地存储足够数量的样本,实现 4 倍、8 倍和 16 倍多采样抗锯齿。可提供质量高、开销很低的抗锯齿
  3. 在每个区块计算完成写回内存时,因为区块足够小,可以通过 CRC 检查将块的颜色与主内存中的当前数据进行比较(这一过程叫做“事务消除”—)如果区块内容相同,则可完全跳过写入,从而节省了 SoC 功耗
  4. 可以采用快速的无损压缩方案(ARM 帧缓冲压缩 (AFBC)),对逃过事务消除的区块的颜色数据进行压缩,从而进一步降低带宽和功耗
  5. 大多数内容拥有深度缓冲和模板缓冲,但帧渲染结束后就不必再保留其内容。如果开发人员告诉 Mali 驱动程序不需要保留深度缓冲和模板缓冲(理想方式是通过调用 glDiscardFramebufferEXT (OpenGL ES 2.0) 或 glInvalidateFramebuffer (OpenGLES 3.0),虽然在某些情形中可由驱动程序推断),那么区块的深度内容和模板内容也就彻底不用写回到主内存中。又大幅节省了带宽和功耗!

Transaction Elimination

Transaction Elimination, 仅粉色区域是需要写入主存的部分,其他部分和之前的数据相同,被跳过

mali硬件支持MSAA x4、x8、x16

缺点

       任何基于区块的渲染方案的主要额外开销是从顶点着色器到片段着色器的中间构建工作集的过程。几何处理阶段的输出、各顶点可变数和区块中间状态必须写出到主内存,再由片段处理阶段重新读取

3. PowerVR TBDR

PowerVRTBDRs

优化点

  1. 开发者绘制不透明物体时不用排序,在 HSR& Depth Test的过程中自动完成排序,early-Z等工作
  2. 相邻像素很可能是共享数据的。在渲染接下来的像素时直接拷贝前一个像素的内存数据,而不是等待内存访问(猜测原因是Tile化后局部缓存的命中率变高了)
  3. PowerVR使用每个tile的尺寸为32x32,但是在开启MSAAx4的设备上,会降级为16x16

PowerVR硬件支持MSAA x4

4. Adreno FlexRender

       Adreno FlexRender相比mali和powerVR的TBDR要复杂一些。在高通的硬件工程师眼中,IMR和TBDR各有优缺点,因此高通的驱动支持了FlexRender,可以根据rendertarget自动选择IMR、TBDR模式

Adreno TBDR

AdrenoTBDR

       Adreno TBDR的优缺点和mali相同。tile的尺寸和MGEM相关(8960上MGEM为512k)。没有工作集的概念,但是每个Tile有一个bin,在Vertex shader后记录一下该primitive覆盖了哪些tile,保存对应的BinIDs,在Rendering Pass,每个Tile依靠binID查询哪些Primitive在该Tile需要渲染,然后是正常渲染流程。

       对于每个Tile,第一个Bin要完整的前向渲染。第二个Bin开始,和之前的MGEM中的depth等信息计算可见性,再进行片段渲染。

Adreno硬件支持MSAA x2、x4