- 啥叫:DrawCall
- 啥叫:SetPassCall
- 啥叫:Batch
- Dynamic Batch - 动态合批
- Static Batch - 静态合批
- GPU Instance - GPU 实例绘制
- SRP Batcher - Unity SRP(Scriptable Render Pipeline) 的合批
- GPU Driven Pipeline
- 动态合批伪代码
- 静态合批伪代码
- 选择合批的优先级
- References
一般我们 在实时渲染中,DC也就是 DrawCall 都会尽可能的降低,因为这会比较直接的降低 CPU 与 GPU 的绘制沟通
啥叫:DrawCall以 OpenGL 为例,就是调用带有绘制功能的 API 的次数
如:DrawCall : 10 次,那就意味着调用了 glDrawXXXX 的 API 10 次
啥叫:SetPassCallUnity 中,就无缘无故多了个叫:SetPassCall 的家伙
其实早在以前的游戏引擎里,没有 pass 这么一个说法,或是 techni 的说法
因为这些都是封装的功能
可以查看我之前学习 OpenGL 时,写的一篇,添加 类似 Unity Pass 功能的文章: LearnGL - 17 - Geometry Shader - 几何着色器 - 直接网页锚点定位到对应的 Pass 实现
一般 OpenGL 中,绘制一个对象,就是提供,VBO,IBO(IBO还不一定需要提供,可以使用 DrawArray 直接通过VBO来绘制,如果通过DrawArrayIndex 之类的就需要 IBO 了,前面的 VBO,IBO 也可以通过 VAO 统一绑定后设置),然后指定 shader(VS,FS,其他的按需提供),就可以调用 DC(DrawCall) API 来绘制就可以了
而 Unity 的 ShaderLab 中可以看到有 Pass 块的代码
其实每个 Pass 块的代码都是一个可以用于完整的 SetDrawState, DrawCall 的过程
因为 ShaderLab 中指定了一部分 DrawCall 前的绘制状态的设置配置,如:ZTest,ZWrite,Cull,Blend,ColorMask,Stencil 等,而 Pass 中的 #param vert XXX, #param frag XXX 就是我们的 VS,FS
最简单的理解 SetPassCall :在绘制此 Pass 前,需要设置的所有状态配置、或是BUFFER设置,都算是 SetPassCall 的内容,或是叫:SetGPUDataBeforeDraw 会更适合理解(在绘制前设置GPU数据,这些数据包括渲染系统,如:DX 或是 OpenGL 的状态值,或是 Buffer 数据)
所以 Unity 多了个:SetPassCall
SetPassCall = SetStateBeforeDraw
啥叫:BatchBatch 直译:批量,的意思
Dynamic Batch - 动态合批在 实时渲染 中,以动态合批为例(Dynamic Batch)一般理解为:为了减少 DrawCall,或是减少 SetPassCall 而将绘制时材质一样(或是说 shader + shader 参数 + 绘制前状态,都一样)的 VBO,IBO,等数据打包到一个大的 VBO、 IBO 中,然后在调用一次 DrawCall,从而提升性能:SetPass 的 State 时,或是多次 Draw API 调用产生过多的 CPU 消耗的性能的问题
但是现在在渲染 API 设置中,调用绘制的 API 的消耗远没有设置渲染状态的 API 的消耗大,比如:OpenGL 中的 glDrawElement 之类 API
这些渲染状态相关的 API,在 unity 叫:SetPassCall
所以下面的静态合批是为了减少 SetPassCall 的
详细可以参考 Unity Dynamic Batch 文档:Dynamic batching
Static Batch - 静态合批静态合批 是将在运行前 或是 发布前,将场景中的 相同材质,并且勾上了 Static Batching 的 MeshRenderer 的 VBO, IBO 都直接放到一个巨大的川村中,并将这个缓存存到文件,具体什么文件格式这个 unity 自己定
这个缓存会记录着每一个 渲染对象的 IBO 的范围,然后在遍历每个渲染对象前,先设置他们同一个渲染状态(也就是材质信息要一直的原因),然后再逐个遍历渲染对象的 IBO,再调用类似 glDrawElement 的 API 来绘制即可,绘制前,要判断这个 渲染对象时是否在视锥体内,如果不在,就不绘制。所以静态合批不是减少 DC,而是减少 DrawState 的设置,在 unity 就是减少 SetPassCall 的设置
Unity 还提供了 Runtime 阶段的实时合并API:StaticBatchingUtility
GPU Instance - GPU 实例绘制(另外还有:Instanced 批量(GPU Instancing Batch)绘制,都算是 Batch 的方式)
所以 Batch 的目的是:将原本需要 多次 SetDrawState + 多次 DrawCall,优化为:1次 SetDrawState + 1次 DrawCall
详细可以参考 Unity GPU Instancing 文档:GPU instancing
SRP Batcher - Unity SRP(Scriptable Render Pipeline) 的合批2021/11/8 - 偶然看到自己这篇文章,而且刚刚好之前在研究 URP,了解到 SRP Batcher,所以再添加一些 “Batch” 的说明,这 SRP Batcher 并不是 DC 上的 Batch,而是类似上面 SetPassCall 的 SetRenderState 的 Batch
详细可以参考:Scriptable Render Pipeline Batcher - Unity 官方 SRP Batcher 介绍
另外,可以查看,某乎上钱总的RenderDoc 抓帧分析:从DX角度看SRPBatcher
GPU Driven Pipeline2021/12/3 - 理论还有另一种方式,GPU Driven Pipeline 中的 One DrawCall per Frame(1帧1Draw),我还没去详细了解过,但是啊,想想也是有可能实现的,具体思路:对 object(vertex array, index array, matrix array, etc.) array, material(shader array, buffers array, etc.) array 都创建一个巨大的数组,一次上传到 GPU,然后 GPU 用每个渲染对象对应的 IDX取到对应的 object, material 信息来渲染,所以1帧1Drawcall理论上是可行的
下面的伪代码中,具体对应 OpenGL 中的代码,可以我之前写的参考:LearnGL - 02 - DrawTriangle - VBO/Shader - 了解一个三角形如何在 OpenGL 中调用绘制
动态合批伪代码//(暂时未实现伪代码)
静态合批伪代码
// jave.lin 伪代码
// =======================================
// jave.lin : 下面模拟运行签 或是 发布前的数据提取,所以这就是为何 包体变大,和内存变大
// =======================================
// jave.lin : 静态合批的单个绘制对象的存储信息
[Serializable]
class StaticObjInfo
{
public uint startIdx;
public uint endIdx;
public Bounds bounds; // jave.lin : 用于绘制时识别是否在 视锥体 内
}
[Serializable]
class StaticBatchInfo
{
public uint materialGUID;
public List objInfos;
public byte[] vbo;
public byte[] ibo;
}
List renderers;
StaticBatchInfo batchInfo;
var vertexBufferStream = new MemoryStream();
var indexBufferStream = new MemoryStream();
uint startIdx = 0;
foreach (var r in renderers) {
// jave.lin : 先将所有顶点位置都转换到 世界坐标,shader 中就不要使用 mul(o2w, v)
var vertexBuffer = new VertexBuffer(r.sharedMesh.vertexBuffer);
for (int i = 0; i
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?