Batch(合批)全面讲解(一)

2023-10-10 15:59
文章标签 讲解 全面 batch 合批

本文主要是介绍Batch(合批)全面讲解(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

批处理(Batch)就是对某对象进行批量的处理,本篇文章给大家介绍一下Batch的基本概念、Batch的特点和导致Draw call性能开销的主要原因。

 

什么是Batch

Batch翻译成中文一般我们称之为“批次”。我们经常用引擎每帧提交的批次数量来作为衡量渲染压力的指标。

 

调用一次渲染API的绘制接口(如:Direct3D的DrawPrimitive/DrawIndexedPrimitive,OpenGL的glDrawArrays/glDrawElements)来向GPU提交使用相同渲染状态的一定数量的三角形的行为为一个渲染批次。从API调用的角度来看,Batch和Draw call是等价的,但是在游戏引擎中他们的实际意义是不一样的:Batch一般指代经过打包之后的Draw call。

 

Batch的特点

我们举一个例子:游戏绘制1百万个物体,而每一个物体具有10个三角形和绘制10个物体,每一个物体具有1百万个三角形,哪个效率更高?

从实践经验中我们了解到,后面一种实际上效率更高。我们可以编写一个测试程序,仅仅绘制一些非常简单的三角形数据,没有光照,没有纹理,去掉任何不必要的开销,仅仅做一些简单的渲染状态的切换和Draw call。这里为什么要做渲染状态的切换而不是直接连续调用Draw call的原因后面会给大家解释。

 

下面的图表出自Nvidia的一次内部分享会的PPT。虽然测试数据使用的硬件配置比较老,但是还是具有很大的参考意义的。表中横轴是Triangles/Batch,表示每一个 Batch中容纳的Triangles数量。纵轴是Million Triangles/s,表示一秒能够绘制的百万级三角形数量。在不同的GPU,相同的CPU的环境下,测试不同大小的Batch所表现出的执行效率的不同。

 

我们通过图表可以看出:

1.随着每个Batch提交的三角形的数量增多,每秒钟能够渲染的三角形数量也在不断增加。

2.在130个Triangles/Batch之前,虽然每个测试环境配置的GPU性能不同,但是随着每个Batch提交的三角形数量增多,每秒能够渲染的三角形数量基本保持一致。

3.在130个Triangles/Batch之后,随着每个Batch提交的三角形的数量的增多,配置高性能GPU的环境每秒钟能够渲染的三角形数量继续线性增长,但是配置低性能GPU的环境处理能力明显降低。

 

在130个Triangles/Batch附近的这个位置数据发生了变化,130个Triangles/Batch之前的效率瓶颈在CPU,GPU有足够的时间来处理提交的三角形。而130个Triangles/Batch之后,配置低性能GPU的环境逐渐出现效率瓶颈。由此可以得出结论:Batch提交的性能消耗主要集中在CPU,而对于不同的Batch大小,CPU的性能消耗是恒定的。

 

我们换一个测试环境。对比两组性能不同的CPU分别配置性能不同的GPU的渲染效率情况:

 

从图表中可以看出:

1.每秒钟能够处理的Batch数量跟CPU的性能有非常直接的关系。

2.GPU的性能对每秒能够处理的Batch数量影响比较小。

3.不同的Batch大小似乎对每秒能够处理的Batch数量没有影响。

 

总结:CPU的性能决定提交Batch的效率,在不给GPU造成渲染压力的前提下,Batch越大越好。

 

我们在实际研发过程中经常遇到CPU效率瓶颈。很多时候正是由于提交的Batch数量过多导致的。如果我们设计的游戏每帧需要绘制的三角形数目是固定的,每个Batch的尺寸很小,而每次提交Batch CPU的性能消耗是一定的。那么只有花费更多的时间才能够绘制完所有的三角形。这就导致了帧率上不去。

 

这就好比一辆从山顶向下运送游客的缆车。缆车的运行速度是恒定的,如果每个车厢装1个人,那么1小时能够运送到山下的人数为N。如果每个车厢装2个人,那么1小时能够运送到山下的人数为2N。

 

每一次Batch的提交就是一次Draw call调用。我们对于提交Batch的特点有了基本的了解之后,再来说一下,是什么导致Draw call在CPU的性能开销。

 

Draw call性能消耗原因

我们的Application中每一次API调用都会经过Application->Runtime->Driver->Video Card(GPU),其中每一步都会有一定的耗时。

 

 

在整个渲染工作的执行过程中主要的计算部件有两个:CPU和GPU。CPU的性能影响着Application、Runtime和Driver的执行效率。GPU的性能主要影响着Vertex处理、Shader计算和一堆Frame Buffer相关的像素操作(如:Blend和Multisample anti-aliasing等)的执行效率。

 

每调用一次渲染API并不是直接经过以上说的所有组件通知GPU执行我们的调用。Runtime会将所有的API调用先转换为设备无关的“命令”(之所以是设备无关的,主要是因为这样我们写的程序就可以运行在任何特性兼容的硬件上了。运行时库使不同的硬件架构相对我们变的透明。),然后将命令缓存在Runtime的Command Buffer中。

 

我在上一篇文章中介绍过这个Command Buffer的概念,但是当时说的是这个Command Buffer是存在于Driver中,由Driver维护的。给大家看一下Direct3D和OpenGL的Runtime的差异,相信大家马上就会明白为什么对于这个Command Buffer的位置怎么说的都有:

 

Direct3D的Runtime是独立的一堆DLL,我们在开发Direct3D应用的时候总是要引入这些动态链接库。而OpenGL的Runtime是和Driver整合在一起的。所以有的时候我们会把OpenGL的Runtime和Driver统称为Driver。那么对于OpenGL,我们既可以说Command Buffer位于Runtime中,也可以说其位于Driver中了。

 

这个Command Buffer的存在不仅仅是为了上一篇文章说的那样,让CPU和GPU能够并行工作。它还有一个很重要的作用:提升渲染效率

 

命令从Runtime到Driver的过程中,CPU要发生从用户模式到内核模式的切换。模式切换对于CPU来说是一件非常耗时的工作,所以如果所有的API调用Runtime都直接发送渲染命令给Driver,那就会导致每次API调用都发生CPU模式切换,这个性能消耗是非常大的。Runtime中的Command Buffer可以将一些没有必要马上发送给Driver的命令缓冲起来,在适当的时机一起发送给Driver,进而在Video Card执行。以这样的方式来寻求最少的CPU模式切换,提升效率。

补充:Shader是在用户模式下编译的,并且编译成设备无关的中间码。Driver解析中间码,然后使用GPU硬件实现我们设计的功能。

 

前面的文章介绍过,渲染状态是一组全局的指导GPU工作的变量集合。如果渲染状态要发生改变,那么要使用之前渲染状态进行渲染的所有Draw call命令都必须已经被执行了。也就是说之前缓冲在Runtime的Command Buffer中的所有Draw call命令必须被刷新。这会强制发生一次CPU从用户模式到内核模式的切换。

 

综合以上,Draw call之间切换Texture、Shader或者Material参数会导致CPU至少两方面的时间消耗:

1.把Draw call及渲染状态切换的API调用转换成设备无关命令耗费的时间(其中还包括命令检查等操作)。

2.刷新Command Buffer导致CPU由用户模式切换到内核模式带来的时间消耗。

 

第一点的时间消耗其实并不大。关键的时间消耗在于第二点。所以一般情况下我们希望Command Buffer缓存尽可能多的命令,然后一次全部提交给GPU执行。

 

到这里大家应该就能明白为什么前面说的实验程序要在每次Draw call之间进行渲染状态的切换了。执行了渲染状态的切换强制Runtime在每次Draw call之后都要进行一次Command Buffer的刷新。如果使用相同的渲染状态,那么Command Buffer会缓存很多Draw call的命令,最后一起发送给Driver执行。这样的话我们就无法测试出每个Batch处理的真实开销了。

 

优化

在产品研发的过程中我们可以预先针对目标平台先设计一个可接受的每帧Batch数量,然后后续美术资源的生产和场景的设计都尽量保证Batch数量不会超标。

在设计Batch数量的时候可以参考以下几个因素:

1.目标平台的CPU性能。

2.我们期望的帧率。

3.Batch提交操作所能够占用的CPU性能百分比(为其他CPU任务留出资源)。

 

顺便介绍一下,移动CPU有一个真八核和假八核的问题。所谓的假八核处理器其实包含两组四核处理器:一个主处理器和一个协处理器。协处理器用来处理一些低耗应用,更省电。当手机运行高能耗应用的时候自动切换到主处理器执行。所以其实同一时间也仅仅只有四个核在工作。高通骁龙820处理器提高了功耗管理,并开始不再采用此前高通骁龙810的八核设计而是改成了四核,但是据说性能是八核的两倍。所以请不要盲目相信CPU核数与性能的关系。。。

 

解决渲染Batch过多的主要方法一个是合批,一个是对Driver进行优化,降低Driver的性能开销。目前比较新的图形API如:DirectX12、Vulkan、Metal,在Driver上的性能消耗已经降低了很多。但是通过合批来降低渲染的Draw call仍然是十分必要的。这也是我们对Draw call优化唯一能够做的事情。关于合批渲染目前主流的游戏引擎中主要有几种常用的做法,下一篇继续说。

这篇关于Batch(合批)全面讲解(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/181521

相关文章

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

STL经典案例(四)——实验室预约综合管理系统(项目涉及知识点很全面,内容有点多,耐心看完会有收获的!)

项目干货满满,内容有点过多,看起来可能会有点卡。系统提示读完超过俩小时,建议分多篇发布,我觉得分篇就不完整了,失去了这个项目的灵魂 一、需求分析 高校实验室预约管理系统包括三种不同身份:管理员、实验室教师、学生 管理员:给学生和实验室教师创建账号并分发 实验室教师:审核学生的预约申请 学生:申请使用实验室 高校实验室包括:超景深实验室(可容纳10人)、大数据实验室(可容纳20人)、物联网实验

如何掌握面向对象编程的四大特性、Lambda 表达式及 I/O 流:全面指南

这里写目录标题 OOP语言的四大特性lambda输入/输出流(I/O流) OOP语言的四大特性 面向对象编程(OOP)是一种编程范式,它通过使用“对象”来组织代码。OOP 的四大特性是封装、继承、多态和抽象。这些特性帮助程序员更好地管理复杂的代码,使程序更易于理解和维护。 类-》实体的抽象类型 实体(属性,行为) -》 ADT(abstract data type) 属性-》成

ispunct函数讲解 <ctype.h>头文件函数

目录 1.头文件函数 2.ispunct函数使用  小心!VS2022不可直接接触,否则..!没有这个必要,方源一把抓住VS2022,顷刻 炼化! 1.头文件函数 以上函数都需要包括头文件<ctype.h> ,其中包括 ispunct 函数 #include<ctype.h> 2.ispunct函数使用 简述: ispunct函数一种判断字符是否为标点符号的函

C++第四十七弹---深入理解异常机制:try, catch, throw全面解析

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】 目录 1.C语言传统的处理错误的方式 2.C++异常概念 3. 异常的使用 3.1 异常的抛出和捕获 3.2 异常的重新抛出 3.3 异常安全 3.4 异常规范 4.自定义异常体系 5.C++标准库的异常体系 1.C语言传统的处理错误的方式 传统的错误处理机制:

深度学习速通系列:深度学习算法讲解

深度学习算法是一系列基于人工神经网络的算法,它们通过模拟人脑处理信息的方式来学习和解决复杂问题。这些算法在图像识别、语音识别、自然语言处理、游戏等领域取得了显著的成就。以下是一些流行的深度学习算法及其基本原理: 1. 前馈神经网络(Feedforward Neural Networks, FNN) 原理:FNN 是最基本的神经网络结构,它由输入层、隐藏层和输出层组成。信息从输入层流向隐藏层,最

C#设计模式(1)——单例模式(讲解非常清楚)

一、引言 最近在学设计模式的一些内容,主要的参考书籍是《Head First 设计模式》,同时在学习过程中也查看了很多博客园中关于设计模式的一些文章的,在这里记录下我的一些学习笔记,一是为了帮助我更深入地理解设计模式,二同时可以给一些初学设计模式的朋友一些参考。首先我介绍的是设计模式中比较简单的一个模式——单例模式(因为这里只牵涉到一个类) 二、单例模式的介绍 说到单例模式,大家第一

[项目][CMP][直接向堆申请页为单位的大块内存]详细讲解

目录 1.系统调用 1.系统调用 Windows和Linux下如何直接向堆申请页为单位的大块内存: VirtualAllocbrk和mmap // 直接去堆上按页申请空间static inline void *SystemAlloc(size_t kpage){#ifdef _WIN32void *ptr = VirtualAlloc(0, kpage << 13,

高斯平面直角坐标讲解,以及地理坐标转换高斯平面直角坐标

高斯平面直角坐标系(Gauss-Krüger 坐标系)是基于 高斯-克吕格投影 的一种常见的平面坐标系统,主要用于地理信息系统 (GIS)、测绘和工程等领域。该坐标系将地球表面的经纬度(地理坐标)通过一种投影方式转换为平面直角坐标,以便在二维平面中进行距离、面积和角度的计算。 一 投影原理 高斯平面直角坐标系使用的是 高斯-克吕格投影(Gauss-Krüger Projection),这是 横