更多内容可见 3.4渲染路径补充-GPU架构
1. Mobile与PC的GPU对比
1.1. 移动端CPU与GPU型号
中国在TOP300中,从手机CPU(SoC)厂商来看,高通占比49.2%,相比于其他厂商有较大的优势,其次是华为28.6%,联发科21%,三星1.1%,其余CPU厂商并未进入TOP300。
GPU上,高通的Adreno与Mali的GPU市场分别占比49.2%与48.2%,而PowerVR-GPU在安卓机器上使用较少,占比仅有2.6%。虽然移动端gpu型号很多,但市场上的厂家主要就三家:Adrno、Mali、PowerVR。
1.2. 功耗对比
桌面端
- 主流性能平台,功耗一般300w
- 游戏主机,功耗一般150-200w
移动端
- 入门和旗舰笔记本,功耗一般100w
- 主流笔记本为50-60w,超极本为15-25w
- 旗舰平板为8-15w
- 旗舰手机为5-8w
- 主流手机为3-5w
总结:如果用最高功耗的桌面和最低功耗的移动端对比,差距有100倍左右的差距
1.3. 带宽对比
绿线为桌面端的带宽演变,将近100起步,最多可到400左右;黑线是移动端,相比之下带宽很小,涨幅也不大(例如骁龙888的移动带宽是32)。因此带宽方面,两者差距也有起码10倍。
2. 名词解释
- System on Chip(Soc)-整合在一起的手机芯片
- Soc是把CPU、GPU、内存、通信基带、GPS模块等等整合在一起的芯片的称呼。常见有A系Soc(苹果),骁龙Soc(高通),麒麟Soc(华为),联发科Soc,猎户座Soc(三星)。去年苹果推出的M系Soc,暂用于Mac,但这说明手机、笔记本和PC的通用芯片已经出现了
- 物理内存(System Memory)-Soc中的手机内存
- Soc中GPU和CPU共用一块片内LPDDR物理内存,就是我们常说的手机内存,也叫System Memory,大概几个G。此外CPU和GPU还分别有自己的高速SRAM的Cache缓存,也叫On-chip Memory,一般几百K~几M。
- 补充:手机上,cpu和gpu共享一个内存地址空间。桌面端两者的内存地址是分开的。
- On-Chip Memory -CPU和GPU的Cache缓存
- 除了系统内存外,cpu和gpu还分别有自己的高速SRAM的Cache缓存,叫作On-Chip Memory,一般为几百K~几百M。不同距离的内存访问存在不同的时间消耗,距离越近消耗越低,读取System Memory的时间消耗大概是On-chip Memory的几倍到几十倍。
- On-Chip Buffer -缓冲区
- 在TB(D)R架构下会存储Tile的颜色、深度和模板缓冲,读写修改都非常快。如果Load/Store指令中缓冲需要被Preserve,将会被写入一份到System Memory中。
- Stall
- 当一个GPU核心的两次计算之间有依赖关系而必须串行时,等待的过程就是Stall
- FillRate
- 即像素填充率,计算方式为
像素填充率 = ROP运行的时钟频率 × ROP的个数 × 每个时钟的ROP可以处理的像素个数
(ROP: Raster Operator Units 光栅处理单元)
- TB(D)R(Tile-Based (Deferred) Rendering)
- 是目前主流的移动GPU渲染架构,对应一般PC上的GPU渲染架构则是IMR(Immediate Mode Rendering )。简单来说就是屏幕被分块渲染。其中Deferred可以看作“延迟”,也就是阻塞+批处理GPU一帧的多个数据,然后一起处理。
- 区分:
- TBR: VS顶点着色器 - Defer - RS光栅化 - PS片元/像素着色器
- TBDR:VS - Defer - RS - Defer - PS
- 通过这第二个Defer,PowerVR的渲染架构真正最大程度上实现了“延后(Defer)PS的执行”,以避免执行不必要的PS计算与相关资源调用的带宽开销,以达到最少的性能消耗和最高的渲染效率。
- 注意:
-
- 有些文章会将TBR叫作TBDR,所以看资料的时候,要搞清楚它说的"D"是指第一个Defer还是第二个。
- 现在的手机基本都是TBDR架构了,但是受到提出TBDR的PowerVR公司的产权保护限制,大家都不敢说是TBDR架构。
3. 立即渲染(IMR)
3.1. 伪代码
- 每个renderpass中有各种DrawCall
- 每个DrawCall里有很多图元
- 我们首先要对每个图元中的顶vertex shader进行处理,如果这个图元没被剔除,就对它里面的fragment进行Fragment shader的处理
3.2. IMR数据流
- 先经过vertex shader的处理
- 再进过一个类似管道(先进先出FIFO)的顺序,最终提交给Fragment Shader
- Fragment Shader最终将结果刷到FrameBuffer里
详细示意图:
PowerVR 图形架构概览:基于图块的渲染 - Imagination (imaginationtech.com)
- 下面虚框:系统内存区域
- 用户数据经过:顶点处理 → 剔除,投影 → 光栅化 → Early Visibility Test → Alpha Test → Late Visibility Test → Alpha Blend 最终将结果刷到FrameBuffer里
- 整个过程直接和系统内存进行交互
4. 基于块元的渲染(TBDR)
4.1. 伪代码
- 第一阶段(Pass one)-分图元
-
- 对每个renderpass里的DrawCall,每个DrawCall里的图元,先进行Vertex shader处理,并生成Primitive List(图元列表)
- 关键在于:分清楚每个块元(Tile)上有哪些图元(Primitve)
- 第二阶段(Pass two)-执行光栅化及后续处理
-
- 对renderpass的每个块元tile,块元中的每个图元primitive,图元中每个片元fragment,执行Fragment Shade处理
-
- 并在完成后,将FrameBuffe从Tile Buffer写回到System Memory中
- 相比传统的IMR架构,不是直接写回System Memory中,而是写到片上内存(OnChip Memory)中
片元不是像素,片元包含了比像素更多的信息,比如深度值、法线、纹理坐标等等信息,片元需要通过一定的测试(比如深度测试),才能最终成为我们所看到图像的像素。
片元着色器Fragment Shader就是像素着色器Pixel Shader。
4.2. TBR数据流
数据通过Vertex shader的处理 → 经过Tiler(图元分好的块) → 刷到一块On-chip Memory(每个块元的内存)上
详细示意图:
- 【中间虚线框】On-Chip Buffer(Memory),Cache缓存
- 【下边虚线框】System Memory ,系统内存
与IMR的区别
- TBDR架构中,多了一步Tiling的过程,这一步是:将顶点处理经过剔除、投影的几何数据刷到系统内存(System Memory)上
- 经过光栅化(Raster)等流水线fragment shader、ROP等,最终把结果刷在片内存(On Chip Memory)上
- 最终片内存上的会刷到FrameBuffer上(FrameBuffer在System Memory上)
动态示意图,对屏幕上的每个块分别渲染:
实际中GPU硬件中乱序执行
4.3. 总结
核心目的
- 为了降低带宽,减少功耗,但渲染帧率上并不比IMR快
优点
- 给消除OverDraw提供了机会:PowerVR有HSR技术,Mali有Forward Pixel Killing技术,都有为了最大限度减少被遮挡的pixel的texturing和shading
- 缓存友好(Cache friendly),在cache的读写速度要比全局内存中快得多(以降低帧率为代价,降低带宽、功耗)
缺点
- 这个操作需要在vertex阶段之后,将输出的几何数据写入到DDR,然后才被fragment shader读取。这之间也就是tile写入DDR的开销和fragment shader渲染读取DDR开销的平衡。另外还有一些操作(比如曲面细分tessellation)也不适用于TBR;
- 如果某些三角形叠加在数个图块(Overdraw),则需要绘制数次。这意味着总渲染时间将高于即时渲染模式。
4.4. TBDR的两个Defer过程
TBDR:VS - Defer(Binning) - RS - Defer - PS
4.4.1. 第一个Defer:Binning(像素合并)
目的:确定哪些图元属于哪些块元渲染
过程:
第二幅图里的红色三角形,只用一个块元就能渲染,所以它只会被分配到一个块元中
第四幅图里的棕色三角形,需要多个块元才能渲染,所以它需要分配到9个块元中一起渲染
举例:红线代表tile的范围,可以显示每个块元的性能参数。
binning过程的耗时占比参考,如果项目中binning过程相比其他耗时长的话,就要考虑一下是不是几何数据过多了。
4.4.2. 第二个Defer:不同GPU 的 Early-Depth-Test
4.4.2.1. 安卓
1. Qualcomm Adreno
采用外置模块LRZ。在正常渲染管线前,先多执行一次VS顶点着色器,生成低精度深度纹理depth texture,以提前剔除不可见的triangles(或像素块?实现细节不知)。说白了,直接用硬件做遮挡剔除occlusion culling,功能类似软光栅遮挡剔除。因为做LRZ时执行VS只需用到顶点位置信息,所以单独抽出position stream,能带来带宽bandwidth和缓存cache的优化。
2. Arm Mali
采用Forward Pixel Kill技术-Mali-T880
发生在Early-z之后
数据模型:先进先出的队列
简单概括一下:队列中有4个Quad(可以理解为2×2像素的平面),每个Quad有屏幕上位置pos的数据和深度Z数据,深度Z越大代表离摄像机越远。根据屏幕上相同位置pos的不同深度z,对不透明的像素进行替换,替换掉深度大的像素,这个过程叫作killed。
4.4.2.2. IOS
PowerVR以及后续自研GPU,采用内置模块HSR。修改原渲染管线架构,增强rasterizer硬件模块为HSR。也就是虚拟出一个射线,当它遇到第一个不透明的物体时就会停下来,这样就会打断后面三角形的后续ps处理。
一张场景图片:
在不经过HSR优化的话,会产生错误的效果。
而经过了HSR优化,效果如下。其中黑色区域是有非透明物体遮挡的部分,看不见后面的物体,而灰色部分是透明物体,可以看见后面的物体。
5. IMR 和 TBR对比
TBR、IMR简化版示意图
图(a)TBR架构
- 几何处理数据形成了FrameData(放在System Memory上)
- 这些Frame Data经过片段处理,结果放在了Tile Buffer上(片的内存上)
- 最后的结果会刷到FrameBuffer中(System Memory上)
图(b)IMR架构
- 对比TBR有以下两种区别
-
- 几何处理数据直接到片段处理,没有中间数据(Frame Data)
- 直接刷到System Memory上了,没有经过片内存(On-Chip Memory)
6. 优化建议
记得在不使用FrameBuffer的时候clear或discard
-
- 这样做主要是为了清空积存在tile buffer上的中间数据(前边提到的Frame Data)
- 对Unity里的rt(render texture)的使用也特别说明一下:当我们不再使用这个rt的时候,尽量调用一次Discard,RenderTexture.DiscardContents()
- 在OpenGl ES上,要善用
glclear
,glInvalidateFrameBuffer
,避免不必要的Resolve(tile buffer刷新到system memory的行为),GL.Clear()
不要在一帧里频繁的切换FrameBuffer的绑定
-
- 本质:减少tile buffer和system memory之间的stall(同步)操作
对于移动平台,建议使用Alpha Blending,而非Alpha Test。
-
- 是一个经验性的结论,在实际使用的过程中应该使用比较两者的表现。通常情况下,移动端应该避免使用Alpha混合来实现透明,如果确实要用,尝试缩小混合区域的覆盖范围
手机上必须用Alpha Test时,先做一遍Depth Prepass(参考Alpha Test 的双pass优化思路)
图片尽量压缩
-
- 例如ASTC 、ETC2
图片尽量走mipmap
尽量使用从Vertex shader传来的Varying变量uv值采样贴图(连续的),不要在Fragment shader里动态计算贴图的uv值(非连续的),否则Cache Miss
在延迟渲染中,尽量利用Tile Buffer(参考传统延迟渲染和TBDR)
如果在Unity中调整ProjectSetting-Quality-Rendering-Texture Quality的不同设置,或者不同分辨率下,帧率有很大的变化,大概率是带宽出问题了
MASS在TBDR下反而是非常快速的
-
- MSAA是硬件上的,发生在片上的。相比FSAA,MSAA在手机上是非常快的
少在Fragment shader中使用discard函数,调用gl_FragDepth从而打断Early-DT的过程
-
- (hlsl中为Clip,glsl中为discard)
在shader使用浮点数精度值时,有目的的区分使用float,half
-
- 优点
-
-
- 带宽减少
- GPU中用的周期数减少,因为着色器编译器可以优化你的代码来提高并行化程度
- 要求的统一变量寄存器的数量减少,这样反而又降低了寄存器数量溢出风险。
- 具体参考:熊大的优化建议、shader数学计算优化技巧
-
在移动端的TBDR架构中,顶点处理部分(Binning过程)容易成为瓶颈
-
- 避免使用曲面细分shader,置换贴图等负操作
- 提倡使用模型LOD,(本质上减少Frame Data的压力)
- Unity中尽早的在应用阶段做umbra(Unity内置)遮挡剔除、gpu的occlusion cull