文章目录

  • 啥叫: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 次


啥叫:SetPassCall

Unity 中,就无缘无故多了个叫: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


啥叫:Batch

Batch 直译:批量,的意思


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 Pipeline

2021/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<StaticObjInfo> objInfos;public byte[] vbo;public byte[] ibo;
}List<MeshRenderer> 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 < r.sharedMesh.vertexBuffer.Count; i ++) {vertexBuffer.pos = mul(r.objToWorldMatrix, r.sharedMesh.vertexBuffer.pos);}// jave.lin : 写入 vertex datavertexBufferStream.WriteVec4(vertexBuffer.pos); // POSITIONvertexBufferStream.WriteVec2(vertexBuffer.ui); // TEXCOORD0vertexBufferStream.WriteVec4(vertexBuffer.color0); // COLOR0vertexBufferStream.WriteVec3(vertexBuffer.normal); // NORMALvertexBufferStream.WriteVec3(vertexBuffer.tangent); // TANGENT...// jave.lin : 记录并写入 index datastartIdx = indexBufferStream.position;indexBufferStream.WriteBytes(r.sharedMesh.indexBuffer.GetRawBytes());batchInfo.objInfos.Add(new StaticObjInfo{startIdx = startIdx,endIdx = indexBufferStream.position,bounds = r.bounds,});
}// 设置 vbo, ibo
batchInfo.vbo = vertexBufferStream.GetRawBytes();
batchInfo.ibo = indexBufferStream.GetRawBytes();// 设置 材质 GUID
batchInfo.materialGUID = renderers[0].sharedMaterial.GetGUID();// jave.lin : 导出文件,这里也就是为何 unity static batching 多了包体会增大的原因
File.WriteBytes("Jave.Lin:这里填写你的导出路径", batchInfo.GetSerializedBytes());
// =======================================
// jave.lin : 下面模拟运行时的读取与绘制,所以这就是为何 包体变大,和内存变大
// =======================================
batchInfo = StaticBatchInfo.Deserialized(File.ReadBytes("Jave.Lin:这里填写你的读取路径"));// 遍历前,先设置材质
var mat = AssetMgr.GetMatFromGUID(batchInfo.materialGUID);
glDrawState(mat); // jave.lin : 假设封装了这么个 API 直接 unity 材质中的 shaderlab 对象的 draw state 的配置。一个 set pass call,当然这里还可以优化:如果一个 staticObj 都不在 camera frustum 内,就不用设置 set pass call 的渲染状态// jave.lin : 遍历前,生成 buffer 信息
uint vbo, ibo;// jave.lin : 生成 buffer,这里一般生成一次就够了,但是这里伪代码,就随意写在这就好
glGenBuffer(GL_ARRAY_BUFFER, batchInfo.vbo, sizeof(batchInfo.vbo), &vbo, ...);
glGenBuffer(GL_ELEMENT_BUFFER, batchInfo.ibo, sizeof(batchInfo.ibo), &ibo, GL_ELEMENT_UINT, ...);
// jave.lin 绑定 buffer
glBindBuffer(vbo);
glBindBuffer(ibo);// 然后遍历绘制
var cam = currentCamerma;
foreach (var info in batchInfo.objInfos) {if (!cam.Visible(info.bounds)) continue; // jave.lin : 不在视锥体内,就跳过绘制glElement(batchInfo.startIdx, batchInfo.endIdx); // jave.lin : 一个dc
}

选择合批的优先级

可以参考:Optimizing draw calls

优先级从上往下(从高到低)

  • SRP Batcher 和 Static Batch
  • CPU Instancing
  • Dynamic Batching


References

  • Draw call batching - Unity 官方 Dynamic/Static Batch
  • GPU instancing - Unity 官方 GPU Instancing
  • Scriptable Render Pipeline Batcher - Unity 官方 SRP Batcher 介绍
  • Batch, Draw Call, Setpass Call - 讲的还不错,但还不够完美
  • Draw Calls vs Batches optimization? [Unity 5] - 该帖子中的 colin299 用户回答得唯一不对的就是 Batch 的理解
  • Unity渲染优化的4种批处理:静态批处理,动态批处理,SRP Batcher 与 GPU Instancing - 在 2022/07/15 发现一篇写的不错的文章
  • URP 系列教程 | 能讲讲如何在 URP 中使用 SRP Batcher 吗?安排上
  • SRPBatcherProfiler.cs - SRP 在 GameView 下的 statices 数据显示有问题,可以使用这个脚本来替代显示

Unity - DrawCall, Batch, SetPassCall区别相关推荐

  1. unity 3分钟理解 批处理和drawcall有什么区别

    3分钟理解 批处理和drawcall有什么区别? 本人技术有限,如有错误,请道友们留言讨论,切勿口吐芬芳. 正文-------------------------------------------- ...

  2. Unity DrawCall优化

    Unity DrawCall优化 一 Mesh Renderer  二 Skinned Mesh Renderer  三 合并要求对比  四 总结  五 场景制作建议 DrawCall优化合并,也叫批 ...

  3. 09_Flink入门案例、word-count程序(java和scala版本)、添加依赖、Flink Streaming和Batch的区别 、在集群上执行程序等

    1.9.Flink入门案例-wordCount 1.9.1.开发工具 1.9.2.编写java版本word-count程序 1.9.2.1.添加Flink Maven依赖 1.9.2.2.编写word ...

  4. Unity DrawCall详解

    在实际项目开发中,提起unity优化,肯定是有DrawCall的相关内容的,下面就讲解一下什么是DrawCall以及如何对DrawCall进行优化操作. 一.什么是DrawCall? 在unity中, ...

  5. dropout,batch norm 区别 顺序

    20210614 https://www.cnblogs.com/hutao722/p/9946047.html 深度学习基础系列(九)| Dropout VS Batch Normalization ...

  6. 神经网络算法学习---mini-batch++++mini-batch和batch的区别

    Batch_Size(批尺寸)是机器学习中一个重要参数,涉及诸多矛盾,下面逐一展开. 首先,为什么需要有 Batch_Size 这个参数? Batch 的选择,首先决定的是下降的方向.如果数据集比较小 ...

  7. Unity矩阵乘法的区别

    Unity世界坐标系的旋转规则是ZXY,自身坐标系的旋转规则是YXZ,可以理解为: LocalToWorldMatrix:Rot(y) * Rot(x) * Rot(z) * P(localPosit ...

  8. Epoch, Batch, Iteration 区别

    Author:龙箬 Computer Application Technology Change the World with Data and Artificial Intelligence ! C ...

  9. Stochastic Gradient Descend和mini batch SGD区别

    SGD 是只对一个数进行更新 mini batch SGD 是对一堆数进行更新

最新文章

  1. 图像处理 100 问!!
  2. linux压缩和解压命令总结
  3. StringCollection FAQ [C#, BCL]
  4. Spark Streaming实现实时WordCount,DStream的使用,updateStateByKey(func)实现累计计算单词出现频率
  5. 【MFC】带下拉菜单的工具栏
  6. 网络IPC:套接字之建立连接
  7. django中怎样生成非HTML格式的内容。
  8. python网页动图_python,tensorflow线性回归Django网页显示Gif动态图
  9. Nacos(一)之简介
  10. Mac python Tesseract 验证码识别
  11. 猎取人心的36条黄金法则
  12. linux 动态输出函数名,控制linux动态链接库导出函数
  13. Paip.论语义分析与语义搜索技术.attilax(艾龙)总结
  14. 数据仓库与数据挖掘 阶段考试复习题
  15. java:单例模式的五种实现方式
  16. java基础学习总结——方法的重载(overload)
  17. Unity3d的场景音效静音处理
  18. 运动型蓝牙耳机什么牌子好、最适合运动的耳机
  19. 动画效果--漫天飞雪
  20. DDoS攻击新玩法 暴雪上演营销新方式

热门文章

  1. docker乞讨之路
  2. 从Trie树(字典树)和后缀树
  3. GraphQL最佳实践
  4. 【Linux】Ubuntu 可以不用安装 yum
  5. 【前端性能】网站性能优化
  6. GPS定位信息的接收
  7. 3D打印发展领域面临的常见问题
  8. GBase 8c数据库技术指标
  9. List初始化、集合复制
  10. 儿童青少年近视防控光明行动课题调研成果发布