【技术美术百人计划】移动端GPU的 TB(D)R 架构基础
一、移动端GPU概况
前言:虽然移动端gpu型号很多,但市场上的厂家主要就三家:adrno、Mali、PowerVR
移动端和桌面端功耗对比
主流性能平台,功耗一般300w,游戏主机,一般为150-200w
旗舰游戏本100w,主流笔记本50w-60w
旗舰手机5-8w,主流手机3-5w
移动端和桌面端带宽对比

桌面端是将近100起步,最多可到400左右
移动端相比之下就少的可怜(骁龙888的移动带宽是32
二、名词解释
SOC(System On Chip)
SOC是把CPU、GPU、内存、通信基带、GPS等模块整合在一起的芯片的称呼。
Sistem Memory
SOC中GPU和CPU共用一块片内LPDDR物理内存,就是我们常说的手机内存,也叫SystemMemory,大概几个G。
此外CPU和GPU还分别有自己的高速SRAM的Cache缓存,也叫OnchipMemory,一般几百k到几M。
不同距离内存访问存在不同的时间消耗,距离越近消耗越低。
OnChipMemory
在TB(D)R架构下会存储Tile颜色、深度和模板缓冲,读写速度都非常快
Stall
当一个GPU核心的两次计算结果之间有依赖关系而必须串行时,等待的过程便是Stall
FillRate
像素填充率=ROP运行的时钟频率 x ROP的个数 x 每个时钟ROP可以处理的像素个数
什么是TB(D)R?
TB(D)R全名是Tile Based (Deferred)Rendering,是目前主流的移动GPU渲染架构,对应一般PC上的GPU渲染架构则是IMR(ImmediateModeRendering)
TB(D)R简单意思
屏幕被分块渲染。(16*16像素或32*32像素)
TBR:VS-Defer-RS-PS
TB(D)R:VS-Defer-RS-Defer-PS
三、立即渲染(IMR)

先经由VertexShader处理,再进过一个类似管道(先进先出)的顺序,最终提交给Fragment Shader,Fragment Shader最终将结果刷到FrameBuffer里

虚线框:系统内存区
用户数据经过:顶点处理 → 剔除,投影 → 光栅化 → Early Visibility Test → Alpha Test → Late Visibility Test → Alpha Blend 最终将结果刷到FrameBuffer里
整个过程直接和系统内存进行交互
TB(D)R宏观上分为两个阶段:
第一阶段执行所有和几何相关的处理,并生成PrimitiveList(图元列表),并确定每个tile上面有哪些Primitive
第二阶段逐块执行光栅化及其后续处理,并在完成后将FrameBuffer从TileBufffer写回到SystemMemory中
四、基于块元的渲染TB(D)R
1、简述
TB(D)R(Tile-Based(Deferrde)Rendering),是目前主流移动GPU的渲染架构,对应一般PC上的GPU架构则为IMR
TBDR简单的意思
屏幕被分块(16*16 or 32*32)渲染
2、TBR和TBDR的区别
TBR:VS - Defer - RS - PS
TBDR:VS - Defer1 - RS - Defer2 - PS
//RS=光栅化 PS=片元shader
通过这第二个Defer,PowerVR的渲染架构真正最大程度上实现了“延后(Defer)PS的执行”,以避免执行不必要的PS计算与相关资源调用的带宽开销,以达到最少的性能消耗和最高的渲染效率。
注意:
有些文章会将TBR叫作TBDR,所以看资料的时候,要搞清楚它说的"D"是指第一个Defer还是第二个。
现在的手机基本都是TBDR架构了,但是受到提出TBDR的PowerVR公司的产权保护限制,大家都不敢说是TBDR架构。
3、Defer的理解
defer
字面上是:延迟
渲染数据角度来看,Defer就是“阻塞+批处理”(GPU累积一帧的多个数据,最后一起处理)
五、TB(D)R硬件渲染顺序
1、TBDR详细的渲染流程
第一阶段
执行所有与几何相关的处理,并生成Primitive List(图元列表)
并且确定每个Tile(块)上有哪些Primitve(图元)
第二阶段
执行光栅化及后续处理
并在完成后,将FrameBuffe从Tile Buffe写会到System Memory中
理解:
第一阶段:
进行“分图”元操作
第二阶段:
相比传统的IMR架构,不是直接写回System Memory中,而是写到片上内存(OnChipMemory)中
2、示意图
a.简略示意图

pipeline:
数据通过Vertex shader的处理 → 经过Tiler(图元分好的块) → 刷到一块On-chip Memory(每个块元的内存)上
第一阶段(Pass one)
对每个renderpass里的DrawCall,每个DrawCall里的图元,先进行Vertex shader处理
关键在于:分清楚每个块元(Tile)上有哪些图元(Primitve)
第二阶段(Pass two)
对每个块元执行每个renderpass
对每个块元上的图元进行Fragment Shade处理
个人总结:
理解的重点在于搞清楚什么是块元(Tile),什么是图元(Primitve)
b.TBDR详细示意图

【上边虚线框】On-Chip Buffer(Memory),代表片上的内存
【下边虚线框】System Memory ,是系统内存
个人理解:
顶点shader统一渲染后,将其分为多个块元(如16*16像素大小),每分好一个便写入内存,全部分好后再传入GPU统一进行包括片元着色在内的其他操作。操作完成后的块元,传入OnChipBuffer,拼合成完整的图像,再传入内存。
本质上是因为手机的GPU与CPU共用内存,且GPU的On-Chip Memory内存过小,为了在使用共用内存时减少通信延迟所使用的方法
与IMR的区别
TBDR架构中,多了一步Tiling的过程,这一步是:将顶点处理经过剔除、投影的几何数据刷到系统内存(System Memory)上
经过光栅化(Raster)等流水线fragment shader、ROP等,最终把结果刷在片内存(On Chip Memory)上
最终片内存上的会刷到FrameBuffer上(FrameBuffer在System Memory上)
//仔细多看几遍图,概念对应上,都在图上了
c.总结TBR
核心目的
为了降低带宽,减少功耗,但渲染帧率上并不比IMR快
优点
给消除OverDraw提供了机会:
PowerVR有HSR技术,Mali有Forward Pixel Killing技术,都有为了最大限度减少被遮挡的pixel的texturing和shading
缓存友好(Cache friendly),在cache的读写速度要比全局内存中快得多(以降低帧率为代价,降低带宽、功耗)
缺点
binning过程是在vertex阶段之后,将输出的数据写到系统内存(DDR)上,然后才能被fragment shader读取。
这样一来几何数据过多的管线,容易在此处有性能瓶颈
如果某些三角形叠加在数个tile(块)上,会被绘制数次。
这样就意味着:总渲染时间多于IMR
六、IMR和TB(D)R对比
TBR、IMR简化版示意图

图(a)TBR架构
几何处理数据形成了FrameData(放在SystemMemory上)
这些Frame Data经过片段处理,结果放在了TileBuffer上(片的内存上)
最后的结果会刷到FrameBuffer中(SystemMemory上)
图(b)IMR架构
对比TBR有以下两种区别
几何处理数据直接到片段处理,没有中间数据(Frame Data)
直接刷到System Memory上了,没有经过片内存(On-Chip Memory)
七、Binning过程(第一个Defer)
目的:确定哪些图元属于哪些块元渲染

注释:
第二幅图里的红色三角形,只用一个块元就能渲染,所以它只会被分配到一个块元中
第四幅图里的棕色三角形,需要多个块元才能渲染,所以它需要分配到9个块元中一起渲染
一些参考:一款老的骁龙gpu调试器下的binning参数

红线代表tile的范围,可以显示每个块元的性能参数
binning过程的耗时占比参考

如果你的项目中binning过程相比其他耗时长的话,就要考虑一下是不是几何数据过多了
八、不同GPU的Early-Depth-Test(第二个Defer)
Android
QualcommAdreno采用外置模块LRZ。
在正常渲染管线前,先多执行一次VS,生成低精度的DepthTexture,以提前剔除不可见的triangels。
说白了,就是用硬件做occlusion culling,功能类似软光栅遮挡剔除。
Mail的FPK
Arm Mail采用的是Forward Pixel Kill技术
管线位置位于Early-Z之后,是一个先进先出的队列。
队列中有4个Quad(可以理解为2×2像素的平面),每个Quad有屏幕上位置的数据和Z数据
Z越大代表离摄像机越远
根据屏幕上相同位置(pos)的不同z,对不透明的像素进行替换(有近的就不渲染远的),这个过程叫作killed
PVR的HSR
HSR = Hide Surface Removal隐形面剔除
大体实现原理:虚拟出一个射线,当它遇到第一个不透明的物体时就会停下来,这样就会打断后面三角形的后续ps处理
九、优化建议
在不使用FrameBuffer的时候clear或discard
主要是清空在TileBuffer上的中间数据,所以在unity中对RenderTexture的使用也特别描述了在不使用此RT前,调用一次Discard。在OpenGL ES上善用glClear,gllnvalidateFrameBuffer避免不必要的Resolve行为。(Resolve就是TileBuffer(片内存)刷新到SystemMemory(系统内存))
不要在一帧内频繁切换FrameBuffer的绑定
本质上是减少TileBuffer和SystemMemory之间的stall操作
对于移动平台上,建议使用AlphaBlend,而不是AlphaTest。
在实际使用中,你应该分析比较Alpha测试和Alpha混合的表现,这取决于具体内容。因此你需要测量,通常在移动平台上应避免使用AlphaBlend来实现透明。需要进行Alpha混合时,尝试缩小混合区域的覆盖范围。
当手机上必须要AlphaTest时,先做一遍DepthPrepass
参考Alpha Test 的双pass优化思路
图片尽量压缩,如ASTC,ETC2
图片尽量走Mipmap
尽量使用从VertexShader传来的Varying变量uv值采样贴图(连续的)
尽量不要在FragmentShader里动态计算贴图的uv值(非连续的),否则CacheMiss
在延迟渲染中尽量使用TileBuffer
参考传统延迟渲染和TBDR
如果你在Unity里调整ProjectSetting/Quality/Rendering/TextureQuality不同的设置,或者不同的分辨率下,帧率变化较大,那么十有八九是带宽出现问题。
MSAA在TBDR下反而是非常快速的
MSAA是硬件上的,是发生在片上的。相比FSAA,MSAA在手机上无疑是更快的
少在FragmentShader使用discard函数,调用gl_FragDepth从而打断Early-DT(HLSL中为clip,GLSL中为discard)
在shader里使用浮点精度,有目的的区分float和half。
带宽会减少
GPU使用周期减少,因为着色器编译器可以优化你的代码以提高并行化程度
要求的统一变量寄存器数量减少,这反过来又降低了寄存器数量溢出的风险。具体有哪些数据类型适合用half或float或fixed,参考【熊大的优化建议】和【shader数学计算优化技巧】
在移动端TB(D)R架构上,顶点处理部分,容易成为瓶颈,
避免使用曲面细分shader置换贴图等负操作,提倡使用模型LOD,本质上减少FrameData的压力,unity中尽早在应用阶段借助umbra遮挡剔除,参考【occlusion-culling-tutorial】【天涯明月刀手游优化】
十、参考链接
[GPU性能指标]
https://www.gpuinsight.com/gpu_performance/
[三星的GPU-FrameBuff指导]
https://developer.samsung.com/galaxy-gamedev/resources/articles/gpu-framebuffer.html
[英伟达的TBR教学文章]
https://www.techpowerup.com/231129/on-nvidias-tile-based-rendering
[ARM的TBR教学文章]
https://developer.arm.com/solutions/graphics-and-gaming/developer-guides/learn-the-basics/tile-based-rendering/single-page
[苹果OpenGL程序开发指南]
https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/Performance/Performance.html
[OpenGL Insights]
https://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-TileBasedArchitectures.pdf
[知乎文章:Tile-based和Full-screen方式的Rasterization相比有什么优劣]
https://www.zhihu.com/question/49141824
[移动设备GPU架构知识汇总]
https://zhuanlan.zhihu.com/p/112120206
[再议移动平台的AlphaTest效率问题]
https://zhuanlan.zhihu.com/p/33127345
[移动平台GPU硬件学习与理解]
https://zhuanlan.zhihu.com/p/347001411
[PowerVR开发者指南]
http://cdn.imgtec.com/sdk-documentation/Introduction_to_PowerVR_for_Developers.pdf
[Performance Tunning for Tile-Based Architecture Tile-Based架构下的性能调校]
https://www.cnblogs.com/gameknife/p/3515714.html
[TBDR的HSR流程细节和使用AlphaBlend的效率提升程度]
https://www.zhihu.com/question/49141824
[当我们谈优化时,我们谈些什么]
https://zhuanlan.zhihu.com/p/68158277
https://edu.uwa4d.com/course-intro/1/179
[Alpha Test的双pass 优化思路]
https://zhuanlan.zhihu.com/p/58017068
[个人收藏]
https://github.com/killop/anything_about_game#gpu-architecture
[Adreno Hardware Tutorial 3: Tile Based Rendering]
https://www.youtube.com/watch?v=SeySx0TkluE&pbjreload=101
[Mali GPU的独有特性]
https://www.cnblogs.com/hamwj1991/p/12404551.html
[Mali-T880]
http://grmanet.sogang.ac.kr/ihm/cs170/20/HC27.25.531-Mali-T880-Bratt-ARM-2015_08_23.pdf
[熊大的优化建议]
http://www.xionggf.com/post/unity3d/shader/u3d_shader_optimization/
[GPU画像素的顺序是什么]
https://zhuanlan.zhihu.com/p/22232448
[Tile-based Rasterization in Nvidia GPUs with David Kanter of Real World Tech]
https://www.youtube.com/watch?v=Nc6R1hwXhL8&t=973s&pbjreload=101