|
本章节主要是让大家对Unity3d中的GPU-Instancing的原理进行一个整体的了解。
跟着文章由浅入深,相信只要看完整个系列的文章就能轻松搞定在大规模的程序开发中使用GPU Instancing的问题,本文主要来源一个小众《元宇宙》项目。
对应Demo用例下载:链接: https://pan.baidu.com/s/1qoiiHGRbe_skt6Nercub8Q?pwd=9hf0 提取码: 9hf0
一、什么是Draw Call
1、官方手册解读
老样子先看相应Optimizing draw calls - Unity 手册
优化Draw calls
要在屏幕上绘制几何图形,unity会调用图形API进行处理。一个Draw call会告诉图形API需要绘制什么以及使用什么方式进行绘制。每个Draw call包含了图形API所需要的纹理,阴影以及缓冲区的绘制信息。大量的Draw call会消耗大量的资源,但Draw call的准备通阶段要比Draw call本身消耗更多的资源。
二、一次DrawCall在做什么
以上是为自己的翻译,我们可以通过以下的Gif来感受一下GPU与CPU的通信,简单的来说Draw call其实是CPU与GPU的通信方式,他们通过CommandBuffer作来通信的“信道”,其实每一次GPU与CPU的通信并没有我们想得的那么简单叫一个Draw Call其中的过程有很多步骤,所以Unity3D又叫作Batch(批次)。
如下图所示:
CPU与GPU的一次Draw Call
GPU渲染速度远远高于CPU提交命令的速度,如果一帧中间DrawCall数量太多,CPU就会在设置渲染状态-提交drawcall上花费大量时间,造成性能问题,这里的性能问题其实是GPU在等等GPU的处理。
1、举个例子(测试用例1)
我们可以通过Unity3D的一个案例来说明
想要用例下载见百度网盘:
链接: https://pan.baidu.com/s/1qoiiHGRbe_skt6Nercub8Q?pwd=9hf0 提取码: 9hf0
创建Unity工程,在场景中创建对象,并使用如下代码
using UnityEngine;
public class CreateCube : MonoBehaviour
{
[SerializeField]
private GameObject _instanceGo;//需要实例化对象
[SerializeField]
private int _instanceCount;//需要实例化个数
[SerializeField]
private bool _bRandPos = false;//是否随机的显示对象
// Start is called before the first frame update
void Start()
{
for (int i = 0; i < _instanceCount; i++)
{
Vector3 pos = new Vector3(i * 1.5f, 0, 0);
GameObject pGO = GameObject.Instantiate<GameObject>(_instanceGo);
pGO.transform.SetParent(gameObject.transform);
if(_bRandPos)
{
pGO.transform.localPosition = Random.insideUnitSphere * 10.0f;
}
else
{
pGO.transform.localPosition = pos;
}
}
}
}
创建一个NormalCubeCreate的空节点放挂上以上代码
并创建一个Cube把Cube制作成prefab后播放到Instance Go中
运行后的效果
感谢使用腾讯文档,您粘贴的区域不支持图片插入。
2、用例分析
通过Statistics我们可以看到Batches为12这里的Batches为Draw Call的次数
打开Frame Debug: Frame Debug可以显示每一帧渲染时CPU与GPU的一些绘制信息
相应 的Frame Debug下的数据显示
通过以上的测试我们在RenderForward.RenderLoopJob中看到10个Draw Mesh NormalCube(Clone)的提交,相当于在同一帧中CPU与GPU提交了10次的绘制(Draw call),每画一个Cube就有一次DrawCall的调用。
那有没有更优的解决方案呢,如一次就把这十个对象都绘制上,当然有的,但是是有前提的那就是我们说的材质和网格需要相同(但不完全如此总归是有此约束)。
三、更优的Draw Call处理
1、优化方案
通过以上的GIF我们可以这么理解,我们可以把需要绘制的相同内容同时放到CommandBuffer中再通知GPU进行绘制,这样可以有效的优化每绘制一个对象就调用一个转态转换让GPU进行显示效率要高。
2、举个例子
我们可以通过Unity3D的一个案例来说明
创建Unity工程,在场景中创建对象,并使用测试用例1的代码
重新创建一个Prefab命名InstanceCube,在所使用的Material中选择Enable GPU Instancing
创建一个GameObject重名称为InstanceCubeCreate挂上CreateCube组件,把InstanceCube
运行效果如下
2、用例分析
从Statistics中我们可以看到Batches变成了3,而RenderForward.RenderLoop.Job为1,我们仅仅只是在Cube中色选了Enable GPU Instancing就达到了我们想要的效果。其中Frame Debug中显示的Draw Mesh(Instanced) 和官方文档中提到的
表现完全一至看来是GPU Instancing起到了正向的作用。
四、总结
经过以上的使用我们初小掌握了GPU Instancing的使用,在Material中只要把开“Enale GPU Instancing”就有如上的运行效果,原来GPU Instancing如此的简单。似乎到此咱们就结束了相应的课程。
咱只是入了个门
咱只是入了个门,路漫漫兮......
在下面的章节中我会给大家介绍,GPU Instancing中的一些限制,这些限制我们要如何绕过去?当然方法很多。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|