合批/批量渲染 (Batch)、实例化Instancing

可以简单的理解为:批量渲染是通过减少CPU向GPU发送渲染命令(DrawCall)的次数,以及减少GPU切换渲染状态的次数,尽量让GPU一次多做一些事情,来提升逻辑线和渲染线的整体效率。但这是建立在GPU相对空闲,而CPU把更多的时间都耗费在渲染命令的提交上时,才有意义。

合批最重要的前提:材质必须相同!!!
合批是节省了CPU的相关准备工作的工作量。

合批后,经过VS,PS,尝试测试,模板测试后,此时已没有了纹理,顶点,索引的概念,只剩下一个个孤立的像素,各像素间没有任何关系了。像素送到GPU后进行批量处理,呈现到屏幕硬件上。因此合批与GPU没有任何关系,也几乎没有影响。不管是一批还是多批,最终在此帧送到GPU的像素数量是相等的,数据是相同的。分成多批,是一帧内将像素数据分多次提交给GPU。合批与否,对GPU的影响仅是像素到达的慢了还是快了,几乎不影响GPU的性能。

一、离线合批(Offline Batch)

离线合批就是在游戏运行前,先用工具把相关资源做合批处理,以减轻引擎实时合批的负担。
适合离线合批的是静态模型和场景物件。如场景地表装饰面:石头/砖块等等。
离线合批方式有:

  1. 美术利用专业建模工具合批。如3D Max/Maya等。
  2. 利用引擎插件或工具。如Unity的插件MeshBaker和DrawCallMinimizer,可以将静态物体进行合批。
  3. 自制离线合批工具。如果第三方插件无法满足项目需求,就要程序专门实现离线合批工具
    在这里插入图片描述
    在meshoptimizer开源库中就有非常好的mesh合并,先合并同一种Materials,删除冗余数据然后同一种Materials的mesh进行合并!

二、实时合批(Runtime Batch)

Unity引擎内建了两种合批渲染技术:Static batching(静态合批)和Dynamic batching(动态合批)。

第一 静态合批;
相同材质而且不能发生变换(旋转平移等等)
第二 动态合批;
第三 实例化渲染;
Unity有两种合批,动态和静态,静态本质就是对标记为static的Mesh自动合并,没有任何区别。而动态合批则是将数份Mesh的数据复制粘贴到一起,也就是实时的,每一帧都合并。
所以一句话总结动态合批:用复制数据的性能消耗换取提交Drawcall的性能消耗。而现代计算机拥有更加优秀的硬件和API,所以动态合批在新设备上已经呈现负优化的趋势了。官方也注意到了这一点所以开发了新的SRP Batcher系统,SRP Batcher的思想在于放弃对模型的合并,转而利用现代API“提交渲染请求不昂贵,提交渲染所需数据和改变渲染状态更昂贵”这样的特性,提供了SRP Batcher:
最后总结一下:静态合批就是Mesh合并,动态合批是每一帧都进行一遍的Mesh合并。

静态合批的利弊:

静态合批采用了以空间换时间的策略来提升渲染效率。

其优势在于:网格通常在预处理阶段(打包)时合并,运行时顶点、索引信息也不会发生变化,所以无需CPU消耗算力维护;若采用相同的材质,则以一次渲染命令,便可以同时渲染出多个本来相对独立的物体,减少了DrawCall的次数。在渲染前,可以先进行视锥体剔除,减少了顶点着色器对不可见顶点的处理次数,提高了GPU的效率。

其弊端在于:合批后的网格会常驻内存,在有些场景下可能并不适用。比如森林中的每一棵树的网格都相同,如果对它采用静态合批策略,合批后的网格基本等同于:单颗树网格 x 树的数量,这对内存的消耗可能就十分巨大了。

总而言之,静态合批在解决场景中材质基本相同、网格不同、且自始至终都保持静止的物体上时,很适用。

动态合批与静态合批的区别:

1、动态合批不会创建常驻内存的“合并后网格”,也就是说它不会在运行时造成内存的显著增长,也不会影响打包时的包体大小;
2、动态合批在绘制前会先将顶点转换到世界坐标系下,然后再填充进顶点、索引缓冲区;静态合批后子网格不接受任何变换操作,仅手动合批后的Root节点可被操作,因此静态合批的顶点、索引缓冲区中的信息不会被修改(Root的变换信息则会通过Constant Buffer传入);
3、因为2的原因,动态合批的主要开销在于遍历顶点进行空间变换时的对CPU性能的开销;静态合批没有这个操作,所以也没有这个开销;
4、动态合批使用根据渲染器类型分配的公共缓冲区,而静态合批使用自己专用的缓冲区。

GPU Instancing

GPU Instancing 就是常说的实例化没有动态合批那样对网格数量的限制,也没有静态网格那样需要这么大的内存,它很好的弥补了这两者的缺陷,但也有存在着一些限制,我们下面来逐一阐述。与动态和静态合批不同的是,GPU Instancing 并不通过对网格的合并操作来减少Drawcall,GPU Instancing 的处理过程是只提交一个模型网格让GPU绘制很多个地方,这些不同地方绘制的网格可以对缩放大小,旋转角度和坐标有不一样的操作,材质球虽然相同但材质球属性可以各自有各自的区别。如下图两个图如果使用mesh合并用处不是很明显!如果像这样绘制模型的大量实例(Instance),你很快就会因为绘制调用过多而达到性能瓶颈。与绘制顶点本身相比,使用glDrawArrays或glDrawElements函数告诉GPU去绘制你的顶点数据会消耗更多的性能,因为OpenGL在绘制顶点数据之前需要做很多准备工作(比如告诉GPU该从哪个缓冲读取数据,从哪寻找顶点属性,而且这些都是在相对缓慢的CPU到GPU总线(CPU to GPU Bus)上进行的)。所以,即便渲染顶点非常快,命令GPU去渲染却未必,而且大量的顶点顶点着色器MVP变换等压力也挺大的!如果我们能够将变换处理好的数据一次性发送给GPU,然后使用一个绘制函数让OpenGL利用这些数据绘制多个物体。这就是实例化(Instancing)。
实例化这项技术能够让我们使用一个渲染调用来绘制多个物体,来节省每次绘制物体时CPU -> GPU的通信,它只需要一次即可。如果想使用实例化渲染,我们只需要将glDrawArrays和gl
DrawElements的渲染调用分别改为glDrawArraysInstanced和glDrawElementsInstanced就可以了。这些渲染函数的实例化版本需要一个额外的参数,叫做实例数量(Instance Count),它能够设置我们需要渲染的实例个数。这样我们只需要将必须的数据发送到GPU一次,然后使用一次函数调用告诉GPU它应该如何绘制这些实例。GPU将会直接渲染这些实例,而不用不断地与CPU进行通信。

请添加图片描述

这里有500+的cube其实都是在同一个cube复制出来只需要记录一个Transform就可以啦,给他一个变换矩阵、可以是缩放、平移、旋转等等;包括gltf里面也可以直接实现实例化!
水就是用了实例化技术translation里做了平移。更好的学习例子openglleran 实例化小行星带
请添加图片描述

Draw Call 本质与现代的DC问题

当Mesh数据大小一样的前提下 draw call 渲染指令传的次数越少越快! (就好像 30W 顶点 cpu 总共向GPU传递3次 每次10W 快 ?还是 cpu 总共向GPU传递1000次 每次300个快?) GPU一直在等待CPU!本质上就是说现代的gpu速度已经远远甩开啦cpu的速度 不是一个数量级的。一次性处理30个顶点和一次性处理10000顶点没有什么区别 GPU早就处理完了一直在等待Cpu传(或者这样表述:

“现代GPU的计算性能其实性能很高,并行渲染计算速度很快,如果CPU向GPU传递的数据不够的话,GPU的计算能力不能被充分利,所以瓶颈在CPU
不是在GPU ,CPU与GPU数量级相差是的三次方到六次方 甩开的很远”


更详细是这样的: cpu到gpu的通信是 先吧数据加载到 显存 设置渲染状态 然后cpu调用渲染命令才开始(dc)才到渲染管线。就是gpu已经处理完了 指令池中的指令 还没等到cpu的指令 cpu还在设置渲染状态等等(也是俗称的 上下文比如opengl的上下文或者dx )相同材质 光照 渲染状态是一致的 所以合批要相同材质的 mesh才可以 渲染状态很多比如深度测试与写入 、透明的混合 等等。就像上面举的例子传递3次每次10W顶点,CPU渲染状态只需要设置三次。但是如果是传1000次每次300顶点CPU设置渲染状态需要1000次,GPU利用率很低而CPU太浪费了,因为这1000次中有大部分相同!
对于刚刚上面说的需要更正一点的是:数据和指令并不是串行执行的。指令推送到PushBuffer与数据DMA传送是可以同时进行。在OpenGL这里,你的渲染数据也不一定存储在显存。具体什么时候做数据调度和流水线的驱动实现有关。而且现代的图形API都已经可以做到指令并行执行,DC已经不是一个瓶颈了,更何况有IndirectDraw这些trick,可以做GPU driven rendering。只不过需要自己去注意资源和状态之间的互斥关系,自己来处理锁

对于性能分析:GPU等CPU出现的情况就是CPU太忙GPU太闲,一般CPU测回执行绘制指令的组织,包括各种算法提升渲染速度尽可能的减少绘制指令这些都是有开销的,或者在极端一点,外面一个多线程应用占用了99%的CPU资源,你能提供分摊到组织渲染的算力少的可怜所以你要分析你们的软件有没有别的功能占用了CPU。

参考资料:
想对硬件cpu gpu了解的分享一个好文章 前同事写的虎牙大佬
深入GPU硬件架构及运行机制
游戏图形批量渲染及优化:Unity静态合批技术
动态合批和静态合批的区别
实例化

猜你喜欢

转载自blog.csdn.net/chenweiyu11962/article/details/121340711