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

2023-11-14

可以简单的理解为:批量渲染是通过减少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静态合批技术
动态合批和静态合批的区别
实例化

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

合批/批量渲染 (Batch)、实例化Instancing 的相关文章

  • 如何高效学习 Python 的第三方库?

    你好 我是你们的老朋友 这篇文章来自同学的提问 问题就是如何高效学习 Python 的第三方库 我在此总结如下 通用思路 整体思路从以下几个角度入手 阅读文档 第三方库通常都会有相应的文档 文档会介绍这个库的功能 使用方法等内容 所以一定要

随机推荐

  • java 8安装教程

    1 下载JDK a 直接官网下载 不推荐 Java Downloads Oracle 注 现在官方下载需要登录账号 自行注册 登录Oracle账号 如果不想注册 登录账号 可选择百度网盘下载即可 b 或百度网盘 推荐 版本 jdk 8u29
  • fetchxml 汇总_Microsoft Dynamics CRM 4.0 更新汇总2

    946745 You cannot import the customization for an entity to a new system in Microsoft Dynamics CRM BUG 5824 CRM SE 94776
  • wechall writeup

    记录做wechall的题解 转载于 https www cnblogs com babers p 7226535 html
  • python 解决print数组/矩阵无法完整输出的问题

    问题描述 当数组 矩阵过大则只会显示其中一部分 中间则会自动用省略号代替 而我们想要去查看数组 矩阵的具体内容时 则需要将省略号代替的部分展示出来 解决方法 直接在import numpy 加上下面一句代码即可解决 import numpy
  • 几个巧妙的电流检测电路

    在电源等设备中通常需要做电流检测或反馈 电流检测通常用串联采样电阻在通过放大器放大电阻上的电压的方法 如果要提高检测精度 这地方往往要用到比较 昂贵的仪表放大器 以为普通运放失调电压比较大 下面介绍几种巧妙的廉价的电流检测电路 1 三极管电
  • Window XP驱动开发(十六) XP下新建驱动程序工程并编译的第二种方法

    转载请标明是引用于 http blog csdn net chenyujing1234 欢迎大家提出意见 一起讨论 需要源码的可以与我联系 参考文章 http blog 163 com ljm1113 126 blog static 579
  • CButton & CMFCRibbonButton

    CButton public CWnd CMFCRibbonButton继承自CObject 不能添加消息映射
  • vmware虚拟机双网卡 实现本地内网和网络双连接

    一 vmware新建网卡 vmware中 编辑 gt 虚拟网络编辑器 gt 更改设置 网卡配置如下 桥接本地连接 NAT网卡连接网络 二 重启虚拟机 双网卡状态下 ifconfig可以看到有两个ip 可以同时ping通百度 远程公司内网 本
  • Windows系统安装Linux系统教程

    下载VMware workstation 安装地址如下 VMware下载地址 下载好了就是这个样子 我选择的是试用30天 大家也可以找破解版安装包 下载ubuntu ubuntu桌面版下载地址 下载桌面版就好 接下来是安装过程 每一步都有详
  • 如何使用apipost进行接口测试

    在之前的文档中对apipost导入api文档进行了介绍 本次将会给大家介绍一下如何使用apipost对之前导入的接口进行测试 接口测试的介绍 首先先对接口测试进行简单的介绍 接口测试是测试系统组件间接口的一种测试 主要用于测试系统与外部其他
  • Python计算机二级考试备考(重复元素判定)

    编写一个函数 输入参数为列表 如果一个元素在列表中出现了不止一次 这返回True 同时编写调用这个函数和输出测试结果的程序 def isRepeat x if type x type return print 输入错误 请输入列表类型 el
  • 有趣的数据结构算法10——后缀表达式(PRN)介绍及利用栈计算后缀表达式的结果

    有趣的数据结构算法10 后缀表达式 PRN 介绍及利用栈计算后缀表达式的结果 解题思路 实现代码 GITHUB下载连接 在前一天已经利用栈完成2进制到8进制的转换 但是栈的应用方面还有很多 本次我将讲解如何计算后缀表达式的结果 解题思路 后
  • 知识蒸馏-Yolov3

    本人实验 利用上篇的 Learning Efficient Convolutional Networks Through Network Slimming论文阅读笔记 方法 成功将算力8 5G的darknet21 yolo模型 9个类别 m
  • ETSI最新边缘原生白皮书

    ETSI 6月底发布了由 ETSI 多接入边缘计算小组 ISG MEC 成员撰写的关于 MEC 对边缘原生设计的支持 的新白皮书 本白皮书提供了有关边缘原生方法的概述和愿景 作为云原生的自然演变 它详细解释了 Edge Native 的概念
  • Kotlin中标准库函数(apply、let、run、with、also、takeIf、takeUnless)的使用详解

    博主前些天发现了一个巨牛的人工智能学习网站 通俗易懂 风趣幽默 忍不住也分享一下给大家 点击跳转到教程 一 apply函数 apply apply函数可以看作是一个配置函数 你可以传入一个接收者 然后调用一系列函数来配置它以便使用 如果提供
  • 【FFmpeg】ffmpeg+nginx-rtmp实现视频流转发

    1 应用场景 目前的摄像头厂家能提供出来的视频流格式有限 且chrome已经禁止了对flash的支持 导致像硬盘录像机这种只能提供rtsp格式流地址的摄像头无法接入Web应用 所以不得不对视频的流地址进行分发 通过代码对流地址中的数据进行切
  • yolo 车辆测距+车辆识别+单目测距(双目测距)

    基于yolo目标检测算法实现的车前道路中的车辆和行人检测 并且可以估测出目标与本车之间的距离 一 视频展示 yolo车距1 订阅专栏获得源码 提供完整代码 无需看下文 二 单目测距原理 图中有一个车辆 且车辆在地面上 其接地点Q必定在地面上
  • JavaScript数据类型,包括原始类型和引用类型有哪些?

    问题 http bbs daxiangclass com thread 334 htm JavaScript数据类型 包括原始类型和引用类型有哪些 原始类型有五个 Number类型 专门保存数字的类型 可用于进行数学计算等的数值 Strin
  • HTTP2.0之我见

    概述 在讨论HTTP2 X 之前 先要搞清楚HTTP2 0的目的 HTTP 2 协议的主要目的是提高网页性能 以及 HTTP1 0 HTTP1 1 SPDY HTTPS之间的关系 参考 https cloud tencent com dev
  • 合批/批量渲染 (Batch)、实例化Instancing

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