(一)硬件: GPU 相关:
GPU 拥有更多的算术运算单元(ALU),更适合做连续的同质的运算
(1)ALU : 连续的 同质的运算 ( ALU 在移动端被称为 Shader core)
cycle 时钟周期: = 1 / 时钟频率
时钟频率 = (多少)赫兹
带宽 = 时钟频率 * 位宽
移动GPU的“核”: Compute Unit 简称CU 或者是SUC
(1.1)GPU的线程: 和CPU的线程不同;
组成: 由用于着色器输入值的一点内存 和 着色器执行所需的任何寄存器空间组成
(1.2)使用相同着色器程序的线程被分为几组, 一组为一个warp
(1.3) 一个warp被计划用于SIMD处理,由8至64之间的任意数量的GPU着色器内核执行,每个线程都映射到SIMD通道
(1.4) 使用的寄存器的数量越多,warp就可能越少
(1.5)在下面的延迟隐藏中:由于等待而 切换warp, 所以读取texture的操作不要连续,中间插入其它运算指令,:在等待的时候有可以执行的指令,而不是只消耗时间单纯的等待。
(2) GPU并行机制中的 需要知道的名词 SIMD 和SIMT
SIMD : single Instruction Multiple Data(单指令,多数据)
每次只取一条运算指令, 将其应用再多个数据上,同时计算出结果
在SIMD上 : 比如 有16个线程 , 这16个线程执行的指令必须是一样的,
SIMT: single Instruction Multiple Thread
类似SIMD ,但是能够处理指令中的分支情况,虽然是能够处理 ,但是也会造成等待和压力!!!!!
GPU 并行执行一组线程 通称为一个Warp . 一个Warp上可能有 16-64个线程
SIMD上: 比如有16个线程, 可以有8个线程执行if , 8个执行else
(3)从硬件层面来理解 shader中的 if 和 else 的性能消耗:
如上图所示:比如一个Warp 有32个线程, ActiveMask 是一个32bit位的值,每一个线程中对应一个bit值
在执行操作,写入到寄存器前, 会有掩码判断操作, 决定最后的结果是否会被写入到寄存器。
如果 不写入寄存器,那就是空转了一遍,花费时间,降低Warp的速度。。。
(如果if 和else 都会执行,虽然不会写入,但是会空转,也需要花时间,整个Warp的速度就会慢下来)
(3)GPU的数据读取: 是否是连续的数据 决定读取需要几次读取操作
下图中的读取最上面一行数据所需要的次数:1,2,3,4
(4)延迟隐藏
当处于等待状态时: Warp0 卡住了, 后面的 warp1 等 都处于闲置状态。
解决办法:
当Warp0 卡住时, 则把Warp0 冻结, 将Warp0的运算资源转给Warp1去执行,当warp1需要从内存中读取数据 卡住了, 则去用warp2去执行
GPU中每个线程拥有自己独享的寄存器, GPU中的线程切换没有寄存器到内存的数据交换,所以切换的很快
CPU切换线程: 数据需要从寄存器 到内存 内存再到寄存器,开销较大
关于warp切换的详细解释如下图:
(二) 其它
(1)IMR
(2)TBDR ( Tiled basd Deferred rendering) 基于片上的延迟渲染:
TBDR 的产生节省就是 降低能耗 省电和不发热,
Deferred: 这里的意思是 : 等待 + 批处理
将结果先写入On-Chip上:比如往On-Chip上写入10次完毕, 然后 从On-Chip 一次 直接写到内存FrameBuffer
10:1 相对于IMR 节省了带宽
(3) early-z:
(1).prepass 先写入深度
(2)然后各自画各自的东西
(3)多一层pass 就是多一次drawcall
(4)深度测试中 Equal 才Draw
AlphaTest 太耗费不建议使用: 因为它会裁剪, 直接就没有 early-z 的机会
AlphaBlend 也不是太推荐: 虽然没有裁剪 但是 顺序是 从后往前 , 根本就不会让 early-z 起到任何作用
但还是建议: 用 AlphaBlend 代替 AlphaTest
使用:
URP 中SRP 的Batch , 先画深度,然后再执行画它的shader
(5)发热:
SOC发热(不管是CPU 还是GPU导致的)达到一定温度就是“温控” 即CPU 会自动降频, 进而导致帧率下降。。
(5)一些小技巧 关于降低带宽的压力:
1.测试: 如果设置 Quality中的设置, 帧率会发生大的变动,则说明带宽肯定出了问题
2.不使用FrameBuffer的时候 clear 或者 discard
3.renderTexture 当不使用这个rt的之前, 调用一次Discard
4.顶点量在百万触发瓶颈: 不管DC, Shader的多少
(原因: on-chip 片 的大小是固定的, 当顶点的数据量太大, 那么。。。 CPU 就直接访问内存耗费很大,访问很慢,而不是去on-chip上去读取交互数据)
(1)基于 以物体为单位的 剔除 : 视锥剔除
(2)基于层次包围盒
(基于视距的裁剪)
(4)GPU硬件的遮挡查询 Occulation Culling 造成一帧的延迟(用上一帧 预估下一帧的结果)
CPU端的软光栅化: 绘制低分辨率的Z-Buffer的深度测试,
(5)
参数传递的多,读和写 带宽会有压力。
只给Alpha Test 绘制Z-PrePass 可以起到优化作用
(6)
两次采样uv的差异大 或者不连续: 内存访问也是不连续的,没有办法合并内存的访问
FrameBuffer Fetch!!!
硬件PCF反走样
参考:<RealTime Rendering 4th>
RealTime Rendering 中文版