移动设备渲染架构以及GPU优化技巧


前言

目前所有的移动设备基本采用Tile-Based Rendering(基于图块的GPU架构,简称为TBR)渲染主流的渲染架构。本文主要介绍介绍TBR的优缺点。它还将Arm Mali基于图块的GPU架构设计与通常在台式机或控制台中发现的更传统的即时模式GPU进行了比较。
Mali GPU使用基于图块的渲染架构。这意味着GPU将输出帧缓冲区渲染为几个不同的较小子区域,称为图块。然后,它会在完成后将每个图块写出到内存中。使用Mali GPU,这些图块很小,每个图块仅16x16像素。


一、常用的两种GPU渲染架构

目前常用的两种GPU渲染架构是Tile-Based Rendering(基于图块的GPU架构),主要用于移动设备,还有一种是Immediate Mode GPUs(简称IMR, 即时模式架构)传统的台式机GPU架构。

二、Immediate Mode Rendering

1.说明

简称IMR, 也就是Full Screen,因为它不去分Tile,传统的台式机GPU架构通常称为即时模式架构。即时模式GPU将渲染处理为严格的命令流,在每个绘图调用中的每个图元上依次执行顶点和片段着色器。

伪代码如下:

for draw in renderPass:
    for primitive in draw:
        for vertex in primitive:
            execute_vertex_shader(vertex)
        if primitive not culled:
            for fragment in primitive:
                execute_fragment_shader(fragment)

2.优点

硬件数据流和内存交互图:
硬件数据流和内存交互
图上可以看出,顶点着色器以及其他与几何相关的着色器的输出可以保留在GPU内部的芯片,着色器的输出可以存储在FIFO缓冲区中,直到流水线的下一个阶段准备使用数据为止,GPU很少使用外部存储器带宽来存储和检索中间几何结果。
(备注:DDR为数据流, FIFO:First In First Out队列)

IMR的优势分析图:
IMR的优势
IMR的优势是每个primitive直接提交渲染,pipeline没有中断,渲染速度快,pipeline并行起来时,每个Raster core只要负责render分给它的primitive即可,无需其他控制逻辑,只需在pixel shader后对Raster出的pixel做个排序。
(备注:raster为光栅化)

3.缺点

1):如果有很大的图形(主要是三角形)需要被渲染,那framebuffer就会很大,比如对于整个屏幕的颜色渲染或者深度渲染就会消耗很多存储资源,但是片上是没有这么多资源的,因此就要频繁读取DDR。很多和当前frame有关的操作( 比如blending, depth testing 或者 stencil testing)都需要读取这个working set,存储器上的带宽负载可能会非常高,并且这样能耗也很高,对于移动设备来说,这种方式很不利于设备运行;
2):z test跟blending都要频繁从framebuffer里读数据,毕竟framebuffer是位于Memory上,带宽压力和功耗自然高;
3):Overdraw的问题,比如Application在一帧里先画了棵树,然后画了面墙刚好遮住了树,在IMR下树仍然要在Pixel Shader里Sample texture,而Texture也是放在Memory,访存功耗大。

三、Tile-Based Rendering

1.说明

基于图块渲染也称基于瓦片渲染或基于小方块渲染,它是一种在光学空间中通过规则的网格细分计算机图形图像并分别渲染网格(grid)或图块(tile)各部分的过程。这种设计的优点在于,与立即绘制整个帧的立即模式渲染系统相比,它减少了对内存和带宽的消耗。这使图块渲染系统的使用特别常见于低功耗硬件设备。图块渲染有时也被称为中置排序(sort middle)架构,因为它在绘图流水线中间而不是接近结束时进行几何排序。

2.以Mali GPU为例

Mali GPU采用不同的方法来处理渲染过程,这就是所谓的基于图块的渲染方法。此方法旨在最大程度地减少片段着色期间GPU需要访问的外部存储器的数量。
基于图块的渲染将屏幕分成小块,并对每个小图块进行着色着色,直到将其写出到内存中为止。为了使这项工作有效,GPU必须预先知道哪些几何图形有助于每个图块。因此,基于图块的渲染器将每个渲染过程分为两个处理过程:
1):第一遍执行所有与几何相关的处理,并生成图块列表数据结构,该结构指示哪些图元对每个屏幕图块起作用。
2):第二遍将逐块执行所有片段处理,并在完成后将切片写回到内存中。请注意,Mali GPU渲染16x16的图块。

伪代码如下:

# Pass one
for draw in renderPass:
    for primitive in draw:
        for vertex in primitive:
            execute_vertex_shader(vertex)
        if primitive not culled:
            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是否相同,减少重复的渲染。

3.优势

下图显示了TBR的优势:
TBR的优势
对于IMR所有read z/framebuffer,到了TBR通通不需要。TBR只需render完tile后把on-chip的pixel写到frame buffer(不需要写z,因为下一帧不需要用到前一帧的z和color)。这个好处在于TBR将Screen Tiling。这样,每次render的区域变小,小到可以把z/framebuffer搬到on-chip,快,省电。

另外另外两点优势是:
1):TBR给消除Overdraw提供了机会,PowerVR用了HSR技术,Mali用了Forward Pixel Killing技术,目标一样,就是要最大限度减少被遮挡pixel的texturing和shading。
2):TBR主要是 cached friendly, 在cache里头的速度要比全局内存的速度快的多,以及有可能降低render rate的代价,降低带宽,省电

3.缺点

1):这个操作需要在vertex阶段之后,将输出的几何数据写入到DDR,然后才被fragment shader读取。这之间也就是tile写入DDR的开销和fragment shader渲染读取DDR开销的平衡。另外还有一些操作(比如tessellation)也不适用于TBR;
2):如果某些三角形叠加在数个图块(Overdraw),则需要绘制数次。这意味着总渲染时间将高于即时渲染模式。

四、两种渲染架构对比

下图显示了两种渲染架构的对比:
两种渲染架构的对比
说明:
1):IMR的pipeline畅通无干扰,sorting简单,TBR的sorting较复杂,但也给低功耗优化提供了灵活的选择;
2):Geometry的transform和场景的tiling,然后往memory里写入Geometry的数据和每个tile所要rendering的Geometry,相对来说多了内存消耗;
3):PC屏幕大,PC game场景复杂,对Tile list压力大,另外PC追求frame rate,所以很少用TBR,即使用了,遇到复杂游戏场景估计会切换到IMR。

五、移动端GPU优化建议

对于对于GPU优化通常会有两个方向,一个是带宽,另一个是填充率。
1):填充率,可以简单的理解为图形处理单元每秒渲染的像素数量。可以通过对中高低进行分辨率的适配来调整,比如对于低档机子,分辨率(以高为基准)720p的方式,中档机为720p1.2,高档机型为1080(7201.5)的方式来调整。同时也需要控制好Shader的复杂度(顶点着色器和片元着色器)。对于如何分析shader的复杂度,可以通过对shader代码的分析,比如函数的指令级别等进行优化
(参考:http://cdn.imgtec.com/sdk-documentation/PowerVR%20Low%20level%20GLSL%20Optimisation.pdf)
不过最终的性能分析,还是需要借助工具来实现,推荐使用Renderdoc,ios可以直接用XCode,分析每个PS(片段着色器)的耗时,至于如何使用Renderdoc,后续会发文章来介绍。

2):显存带宽,这块很多文章都有介绍,通常大部分图片不要超过1024即可,但场景有些贴图是可以2048的,但要注意是否2048的图是否空出来太多,还有normal贴图,emission图等大小控制,这些贴图通常都是小于等于1024,这块需要约定好一个标准。至于压缩格式,Android和IOS通常使用ASTC_RGBA_6x6或者ASTC_RGBA_8x8,不带Alpha通道为ASTC_RGB_6x6 or ASTC_RGB_8x8,因为目前大部分的Android平台都是支持ASTC,但对于比较老的机型,比如15年,ES3.0,可能会不支持ASTC,目前这块的用户量其实比较少,看项目需求是否坚持采用ASTC,不然就ETC2格式,或者做个兼容。
下图显示了如何通过调整贴图设置去打手机包分析是否带宽瓶颈的一种方式:
在这里插入图片描述
上图是unity引擎上的,Unity的质量选项里可以设置纹理的缩小倍数,它会限制GPU使用的纹理大小(但不会减少内存占用),这样能成倍减少贴图的带宽使用量,但对读屏幕那一部分没有任何影响。如果调低它帧数上升了,就可以说明确实遇到了带宽瓶颈,但没变化,也有可能是因为优化幅度太低了看不出来。

还有一种方式可以减少带宽,mipmap,不过这块会多占用33%的内存大小,如果目前内存使用已经很多了,需要注意这块的使用,通常来说一般会对场景和角色的贴图做mipmap,其他的不做mipmap(会糊掉),做mipmap还是根据实际情况分析是否需要做这块。对于一些自研引擎或者是UE4,会有一种贴图加载的mipmap,比如一开始在包体上有一种贴图就有1024,512,256,128的大小的,通过mipmap偏移去加载对应大小的贴图带显存上,这种方式可以优化内存的大小的。

总结

目前通常移动设备采用的架构都是TBR的,对于GPU优化通常会有两个方向,一个是带宽,另一个是填充率。对于TBT架构来说,影响比较大还是填充率,可以通过调整分辨率,Shader的复杂度(顶点和片元着色器的复杂度)等来控制。而带宽的话,通常大部分图片不要超过1024即可,但场景有些贴图是可以2048的,但要注意是否2048的图是否空出来太多,还有normal贴图,emission图等大小控制,这块需要约定好一个标准。

参考

【1】https://developer.arm.com/solutions/graphics-and-gaming/developer-guides/learn-the-basics/tile-based-rendering/immediate-mode-gpus
【2】https://zh.wikipedia.org/wiki/%E5%9F%BA%E4%BA%8E%E5%9B%BE%E5%9D%97%E6%B8%B2%E6%9F%93
【3】https://zhuanlan.zhihu.com/p/92840602
【4】https://www.zhihu.com/question/49141824
【5】 https://community.arm.com/developer/tools-software/graphics/b/blog/posts/killing-pixels—a-new-optimization-for-shading-on-arm-mali-gpus

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值