Unity基础知识回顾(一)DrawCall、Batch、SetPassCall的区别

内建管线

Unity渲染流程

一次渲染批次(batch):
设置渲染状态(材质、顶点数据等),调用DrawCall绘制

因此当场景中有不同的mesh对象,使用不同的材质时,他们会分别进行渲染设置并调用DrawCall

合批是对这种情况的优化,当多个不同的mesh使用的材质相同时,满足一定条件下,Unity会将他们的模型顶点合并,一次传递给GPU(材质、顶点数据)。
之后再对每个子模型分别调用DrawCall进行绘制。

因此合批会将多个相同材质的mesh在一个批次进行渲染,不会降低drawcall,但是会减少渲染批次。

多pass:
多pass的mesh会被渲染多次,每一个pass会进行一个批次的渲染,并且多pass的mesh不会参与到合批中。
每个pass会调用一次set pass call,但多个相同多pass材质的mesh只会对每个pass执行一次set pass call,而不会对每个mesh执行一次。

因此set pass call我理解的是,视锥体中使用了n个pass(不论多少模型),就会调用n次set pass call。set pass call应该是对pass的一种设置。

而视锥体中有m个使用了i个pass材质的mesh,那么他们产生的batches就是m*i,当然每个材质pass数不同,他们的i也不同。

注: 粒子本身会进行动态合批, 即便使用了多pass的材质, 粒子依然会对每个pass进行合批;

State面板参数的区别

首先得理解setpass call于drawcall batch 的区别以及定义,一次cpu把数据交给gpu处理叫一个drawcall。draw位于setpasscall 和batch中。使用相同材质信息draw call 才能放到一个batch中。一个材质球引用的shader可以有多个pass,因此携带这个材质球的物体会被渲染多次,"激活"一一个pass的过程就是set pass call。因此改变影响 pass的行为都会触发一次set pass call(比如游戏运行中开启阴影进而影响了渲染管线),因此把set pass call 作为影响drawcall的一个标准。

DrawCall

DrawCall:CPU每次调用图像编程接口 glDrawElements(OpenGl中的图元渲染函数)或者 DrawIndexedPrimitive(DirectX中的顶点绘制方法)命令GPU渲染的操作称为一次Draw Call。Draw Call就是一次渲染命令的调用,它指向一个需要被渲染的图元(primitive)列表,不包含任何材质信息,glDrawElements 或者 DrawIndexedPrimitive 函数的作用是将CPU准备好的顶点数据渲染出来。

Batch

把数据加载到显存,设置渲染状态,CPU调用GPU渲染的过程称之为一个Batch。这其实就是渲染流程的运用阶段,最终输出一个渲染图元(点、线、面等),再传递给GPU进行几何阶段和光栅化阶段的渲染显示。一个Batch必然会触发一次或多次DrawCall,且包含了该对象的所有的网格和顶点数据以及材质信息。把数据加载到显存是指把渲染所需的数据从硬盘加载到内存(RAM),再将网格和纹理等加载到显卡(VRAM),这一步比较耗时。设置渲染状态就是设置场景中的网格的顶点(Vertex)/片元(Fragment)着色器,光源属性,材质等。Unity提供的动态合批(Dynamic Batching )合并的就是这一过程,将渲染状态相同的对象合并成一个Batch,减少DrawCall。

SetPassCall

Shader脚本中一个Pass语义块就是一个完整的渲染流程,一个着色器可以包含多个Pass语义块,每当GPU运行一个Pass之前,就会产生一个SetPassCall,所以可以理解为一次完整的渲染流程次数。

由此可见,一个Batch包含一个或多个DrawCall,都是产生是在CPU阶段,而目前普遍渲染的瓶颈恰恰就是CPU,GPU的处理速度比CPU快多了,Draw Call太高,CPU会把大量时间花费在处理Draw Call调用上。如果Batch太大,CPU需要频繁的从硬盘加载数据,切换渲染状态,这个消耗要比DrawCall大,所以后面Unity才逐渐弱化了DrawCall的显示。

再提一下,优化的时候还要关注下Statistics窗口上的三角形数(Tris)和顶点数(Verts),这两个数据也是会影响到性能,比如单个物体的顶点数最好不要超过900,不然会影响到Unity的动态合批。Unity的Statistics窗口上的三角形数(Tris)和顶点数(Verts)并不仅仅是视锥中的梯形内的三角形数和顶点数,而是Camera中 field of view所有取值下的三角形数和顶点数。也就是说,即使当前Game视图中看不到这个 cube,只有 field of view在1-179 范围内都看不到这个cube,stats面板才不会统计,GPU才不会渲染,否则都会渲染,而且Unity不会把模型拆分,这个模型哪怕只有1个顶点需要渲染,Unity也会把整个模型都渲出来。

批处理

静态批处理

原理:

场景中有4个物体,ABCD,如果都勾选静态选项,在进行静态批处理的时候,引擎会判断这四个物体是否共用同一渲染材质。

如果共用同一渲染材质,则会将这四个物体视为可以批处理的对象,引擎会基于单个渲染对象的大小拷贝出3个,总共变为4个mesh,此时这4个mesh会存在一个index buffer中,此时会让资源占用的内存变大4倍,这样在渲染的时候,是将这个更大的mesh传递给GPU进行渲染操作的。

时机:

  1. 在游戏导出的时候,在player setting中勾选static batching,这样在导出包的时候就进行批处理,导出来的包就会比较大;
  2. 在游戏场景中勾选场景物体的static选项,在加载该场景的时候,会进行一次静态批处理的合并,这样导出来的包不大,但是在加载的时候会使得内存变大;

方法:

游戏中如果大量的物体都采用静态批处理,此时会出现很大的内存buffer,如果渲染头和尾部的物体,则会使得渲染数据过大(CPU传递给GPU),带来较大性能损耗,所以可以在游戏中对静态批处理对象进行一个分块的处理。将场景中的对象分成多个块,每个块的大小可以依据一个经验值来设置,此时就会出现多个静态批处理的操作,而不是统一的一个静态批处理操作。具体的分块操作取决于具体的项目的场景大小,可以多次测试得到一个经验值来进行设置。

如果场景自带静态物体,会合并批次,这个大家都知道;如果静态物体是场景加载后再读取预制体动态加载进去的,就不会自动合并批次,需要你加载完后调一下手动合并批次的接口,StaticBatchingUtility.Combine 合并。

动态批处理

条件:

  1. 必须使用相同材质,且材质只能有一个pass;
  2. mesh只能有900个顶点数据;
  3. transform的scale属性不能为负;
  4. 动态批处理默认是关闭的,需要手动开启:Project Setting—Player—勾上Dynamic Batching;

优化方案

  1. 注意小物件减少多pass shader的使用, 而应该使用同一spriteAtlas和材质;
  2. 相同的mesh会被静态批处理合并为一个大的mesh,会增加内存占用,应该合理划分批处理区域;
  3. 静态批处理适用于场景中位置不会改变并且单对象不能太多的物体,像树林,草地这种用动态批处理更适合;
  4. 动态批处理限制较多,顶点数要求,材质要求等等。静态批处理限制较少,是用内存换性能的方法,具体情况具体分析;
  5. 静态批处理会增加额外的内存消耗去存储合并后的Mesh,但原先没有用的Mesh仍保留在内存中,所以可以通过代码动态合并网格后将原先的网格销毁或隐藏;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值