一、当前移动端GPU概况
1.1 移动端和桌面端功耗对比
对于移动端我们通常用soc芯片名称来代指CPU
移动端GPU生产厂商主要是高通adreno,Mali和PowerVR
1.2 移动端和桌面端带宽对比
二、名词解释
- System on Chip(Soc)
- Soc是把CPU、GPU、内存、通信基带、GPS模块等等整合在一起的芯片的称呼。常见的有A系Soc(苹果),骁龙Soc(高通),麒麟Soc(华为),联发科Soc,猎户座Soc(三星),2020年苹果推出M系Soc,暂用于Mac,这说明手机、笔记本、PC的通用芯片已经出现了。
- System Memory
- Soc中GPU和CPU共用一块片内LPDDR物理内存,就是常说的手机内存,也叫System Memory,大概几个G。
- 此外CPU和GPU还分别有自己的高速SRAM的Cache,也叫On-chip Memory,一般几百k-几M。不同距离的内存访问存在不同时间消耗,距离越近消耗越低,读取System Memory的时间消耗大概是On-chip Memory的几倍到几十倍
- (soc上gpu和cpu共享一个(虚拟)内存地址空间)
- On-Chip Memory
- 在TB(D)R架构下会存储Tile的颜色、深度和模板缓冲,读写修改都非常快。
- Stall停滞
- 当一个GPU核心的两次计算结果之间有依赖关系而必须串行时,等待的过程便是Stall。
- FillRate
- 像素填充率 = ROP运行的时钟频率 * ROP的个数 * 每个时钟ROP可以处理的像素个数
- TB(D)R
- Tile-Based (Deferred) Rendering
- 是目前主流的移动GPU渲染架构,对应一般PC上的GPU渲染架构则是IMR(Immediate Mode Rendering)
- 指屏幕被分块(16*16或32*32像素)渲染
- TBR:VS - Defer - Rasterize - PS
- TBDR:VS - Defer - Rasterize - Defer - PS
- Defer字面是延迟,但从渲染数据的角度来看,Defer就是“阻塞+批处理”GPU的“一帧”的多个数据,然后一起处理
三、立即渲染IMR
1 | for draw in renderPass: |
四、基于块元的渲染TB(D)R
TB(D)R宏观上分为2阶段
- 第一阶段执行所有与几何相关的处理,并生成Primitive List,并且确定每个tile上面有哪些primitive
- 第二阶段将逐块执行光栅化及其后续处理,并在完成后将Frame Buffer从Tile Buffer写回到System Memory中。
1 | # Pass one |
五、TB(D)R的硬件渲染顺序
总结
TBR的核心目的是降低带宽,减少功耗,但渲染帧率上并不比IMR块
- 优点
- TBR给消除Overdraw提供了机会,PowerVR用了HSR技术,Mali用了Forward Pixel Killing技术,目标一样,就是要最大限度减少被遮挡Pixel的texturing和shading。
- TBR主要是cached friendly,在cache里读写的速度要比全局内存的速度快得多,以降低render rate的代价,降低带宽,省电
- 缺点
- Binning过程是在vertex阶段之后,将输出的几何数据写入到DDR,然后才被fs读取。几何数据过多的管线,容易在此处有性能瓶颈。
- 如果某些三角形叠加在数个tile上,需要绘制数次。意味着总渲染时间将高于即时渲染模式。
六、Binning过程
Binning过程(类似四叉树)/第一个Defer
确定哪些块元渲染哪些图元
七、不同GPU的Early-Depth-Test
第二个Defer
Android
- Qualcomm Adreno采用外置模块LRZ。在正常渲染管线前,多执行一次vs生成低精度depth texture,提前剔除不可见的triangles。直接用硬件做occlusion culling,功能类似软光栅遮挡剔除/pre-Z
- Arm Mali的FPK(Forward Pixel Killing)
- 发生在Early-Z之后
IOS
PowerVR的HSR
TBDR的HSR实现
- HSR=Hidden Surface Removal
- 对每个被投影光束交接的对象进行排序处理(使用分块减少数据集大小)
- 只有最近的不透明和最近的透明对象需要被渲染
- 余下的片元被剔除
八、优化建议
- 不使用FrameBuffer的时候clear或者discard
- 主要是清空积存在tile buffer上的中间数据,所以在unity里面对render texture的使用也特别说明了一下,当不再使用这个rt之前,调用一次Discard。在Opengl ES上善用glClear,glInvalidateFrameBuffer避免不必要的Resolve(Resolve就是tile buffer刷新到system memory)行为
- 不要在一帧里面频繁切换FrameBuffer的绑定
- 本质上就是减少tile buffer和system memory之间的stall操作
- 对于移动平台,建议使用alpha blend而非alpha test
- 在实际使用中,你应该分析并比较alpha test和alpha blend的表现,因为这取决于具体内容,通常在移动平台上应避免使用alpha混合来实现透明。需要进行alpha blend时,尝试缩小混合区域的覆盖范围。
- 手机上必须要做Alpha Test,先做一遍preZ
- 图片尽量压缩,例如:ASTC,ETC2
- 图片尽量走mipmap
- 尽量使用从Vertex shader传来的Varying变量UV值采样贴图(连续的),不要在FragmentShader里动态计算贴图的UV值(非连续的),否则CacheMiss
- 在延迟渲染尽量利用Tile Buffer
- 如果你在unity里面调整ProjectSetting/Quality/Rendering/Texture Quality不同的设置,或者不同分辨率下,帧率有很多变化,那么多半是带宽出问题。
- MSAA在TBDR下反而是非常快速的。
- 少在fs中使用discard,调用gl_FragDepth从而打断Early-DT(HLSL中为Clip,GLSL中为discard)
- 在shader里面浮点数精度,有目的区分使用float,half;
- 带宽用量减少
- GPU中使用的周期数减少,因为着色器编译器可以优化你的代码以提高并行化程度。
- 要求的统一变量寄存器数量减少,这反过来又降低了寄存器数量溢出风险
- 在移动端TB(D)R架构中,顶点处理部分容易成为瓶颈,避免使用曲面细分shader,置换贴图等负操作,提倡使用模型lod,本质上减少FrameData的压力,Unity中尽早在应用阶段借助umbra遮挡剔除。
作业
结合今天的课程,将最近做的demo继续安卓平台打包,对比使用课上的优化点前后的性能变化。
这是第一次接触安卓平台的打包与性能分析。。关于性能分析使用了Unity的UPR工具。由于没有什么比较完整的demo,之前所有作业都是塞在一个项目里做的,就直接使用这个场景了,虽然感觉也很难触碰到性能瓶颈什么的。而且能够针对上面优化建议处理的点也不太多。于是选择了最直接的图片纹理压缩的部分(正好下一章节就会讲纹理压缩),应该也算非常适合了。
ASTC和ETC2两种压缩格式都非常接近
而当我手动地取消所有纹理的压缩,改为8位浮点精度储存后,发现对性能的影响比想象中大得多。。。
首先是apk文件的大小就增大了一些。其次是最明显的纹理资源峰值这一项,UPR还贴心地提供了说明和优化建议。而且,这里Mipmap甚至已经关掉了。(其实这里做法是相反的,应该关闭压缩开启mip,这样这两个功能在纹理和渲染效率上的作用才是协同的)
那么在手机内存占用上,也发生了相应的变化
ReservedTotal峰值
ReservedGFX峰值
其次就是帧率也发生了下降。这当然也是由于纹理资源数据变大,计算的速度也就下降了。
性能分析工具本身还有很多需要去研究的地方,当然这也需要项目内容的支撑。。。鉴于现在也没有什么合适的内容,这里也只能到此为止了,算是一次小小的尝试。当打包出来的项目在手机上成功渲染出画面的时候,还是非常激动的。。。
(此外,曲面细分与几何着色器生成的草地没能在手机上绘制出来,想必也是手机平台不支持这两个着色器的缘故了。)
参考资料
[1] https://www.bilibili.com/video/BV1Bb4y167zU
【技术美术百人计划】图形 3.7 移动端TB(D)R架构基础
[2] https://blog.imaginationtech.com/a-look-at-the-powervr-graphics-architecture-tile-based-rendering/