MALI Tile-based rendering简单原理介绍
写在最前
关于mali的架构的一点深入了解,将现有的GPU的基本流程和mali的做对比,提出其中的优点与缺点。原文地址:https://developer.arm.com/graphics/developer-guides/tile-based-rendering
传统GPU
传统GPU的架构一般被称为Immediate mode GPU.主要的流程就是vertex shader 和 fragment shader顺序执行,伪代码如下:
for draw in renderPass:
for primitive in draw:
for vertex in primitive:
execute_vertex_shader(vertex)
for fragment in primitive:
execute_fragment_shader(fragment)
数据流是这样的:
优点
主要优点就是vertex的输出能够留在片上,可以被下一阶段直接快速读取。
缺点
如果有很大的图形(主要是三角形)需要被渲染,那framebuffer就会很大,比如对于整个屏幕的颜色渲染或者深度渲染就会消耗很多存储资源,但是片上是没有这么多资源的,因此就要频繁读取DDR。很多和当前frame有关的操作( 比如blending, depth testing 或者 stencil testing)都需要读取这个working set,因此需要的带宽是很大的,并且这样能耗也很高,对于移动设备来说,这种方式很不利于设备运行。
Tile-based GPU
因此mali的GPU提出了Tile-based概念,就是将图像分成16*16的小块。分小块进行渲染,最后写入到DDR,就能够减少读写DDR的频率,进而解决上述问题。不过分块需要知道整个图像的几何学信息,所以操作分成了两步:
- 第一步执行几何学相关的操作,并产生tile list.
- 第二步对每一个tile执行fragment操作,完成之后写入内存
伪代码如下:
# Pass one
for draw in renderPass:
for primitive in draw:
for vertex in primitive:
execute_vertex_shader(vertex)
append_tile_list(primitive)
# Pass two
for tile in renderPass:
for primitive in tile:
for fragment in primitive:
execute_fragment_shader(fragment)
数据流如下:
优点
显而易见,解决了传统模型的带宽问题,因为fragment shader每次都是读取一个小块放在片上,不需要频繁读取内存,直到最后操作完成,再写入内存。甚至还能够通过压缩tile的方法进一步减少对于内存的读写。另外在图像有一些区域固定不动的时候,通过调用函数判断tile是否相同,减少重复的渲染。
缺点
这个操作需要在vertex阶段之后,将输出的几何数据写入到DDR,然后才被fragment shader读取。这之间也就是tile写入DDR的开销和fragment shader渲染读取DDR开销的平衡。另外还有一些操作(比如tessellation)也不适用于Tile-based GPU。
总结
现在屏显的分辨率越来越大从1080p到1440p再到4K,可以遇见的,mali这种架构将在未来大规模使用。
不过也有一些陷阱,开发者需要避开。首先是要合理设置render pass以充分利用这种架构的特点;其次要了解这种几何学分割所能得到的好处。