有关渲染,批处理,图形卡,性能等的信息+ XNA?


12

我知道标题有点模糊,但是很难描述我真正想要的东西,但是这里有。

当涉及到CPU渲染时,性能通常很容易估计和直接,但是当涉及到GPU时,由于缺乏技术背景信息,我一无所知。我正在使用XNA,所以如果理论可以与之相关将非常好。

所以我真正想知道的是,当您执行特定的绘制动作时,何时何地(CPU / GPU)会发生什么?什么是批次?效果,预测等有什么影响?数据是否保留在图形卡上或是否在每个步骤中传输?当谈论带宽时,您是在谈论图形卡的内部带宽,还是从CPU到GPU的管道?
注意:我实际上并不是在寻找有关绘制过程如何进行的信息,这是GPU的业务,我对在此之前的所有开销都感兴趣。

我想了解执行动作X时发生了什么,以适应我的体系结构和实践。

非常感谢任何文章(可能带有代码示例),信息,链接,教程,它们提供了有关如何编写更好的游戏的更多见解。谢谢 :)


2
尽管这最初是XNA,但我已经添加了DirectX标签,因为它是基础技术-它可以帮助您获得更好的答案。另外,请查看此答案,这可能会为您提供一个良好的起点。
Andrew Russell

@AndrewRussell非常感谢:)。实际上,我已经阅读了有关该主题的各种文章,包括该文章。但这并没有涵盖我想知道的所有内容。
Aidiakapi 2012年

Answers:


20

我喜欢从“ 极限 ”的角度来考虑性能。这是一种概念化相当复杂的互连系统的便捷方法。当您遇到性能问题时,会问一个问题:“我达到了什么极限?” (或者:“我绑定了CPU / GPU吗?”)

您可以将其分为多个级别。在最高级别,您拥有CPU和GPU。您可能受CPU限制(GPU处于空闲状态等待CPU),或者受GPU限制(CPU处于GPU等待状态)。这是有关该主题的一篇不错的博客文章

您可以进一步分解。在CPU端,您可能正在对CPU缓存中已有的数据使用所有周期。否则您可能会受到内存的限制,使CPU处于空闲状态,等待数据从主内存中进入(因此请优化数据布局)。您可以进一步分解它。

(虽然我正在就XNA进行性能的广泛概述,但我会指出,通常便宜的引用类型(classnot struct)的分配可能会触发垃圾收集器,这将消耗很多周期,尤其是在Xbox 360上(请参阅此处了解详细信息)。

GPU方面,我将首先向您介绍这篇出色的博客文章,其中包含许多细节。如果您想在管道上获得疯狂的详细信息,请阅读本系列的博客文章。(这是一个简单的)。

简单地说,其中一些比较大的是:“ 填充限制 ”(可以向后缓冲区写入多少像素-通常可以包含多少透支),“ 着色器限制 ”(着色器可以复杂到多少,以及您可以通过它们推送多少数据),“ 纹理获取/纹理带宽限制 ”(您可以访问多少纹理数据)。

而且,现在,我们进入了大问题-这是您真正要问的问题-CPU和GPU必须在何处交互(通过各种API和驱动程序)。松散地有“ 批次限制 ”和“ 带宽 ”。(请注意,第一部分我刚才提到的一系列的进入丰富的细节。)

但是,基本上,每当您调用其中一个函数(或XNA的一部分,如,为您执行此操作)时,就会发生一批您已经知道)。毫无疑问,您已经读过了,每帧可以得到数千张*。这是CPU限制-因此它可以与您的其他CPU使用率竞争。基本上,驱动程序将您要绘制的内容打包在一起,然后发送给GPU。GraphicsDevice.Draw*SpriteBatch

然后是GPU 的带宽。这是您可以在那里传输的原始数据量。这包括批处理附带的所有状态信息-从设置渲染状态和着色器常数/参数(包括诸如世界/视图/项目矩阵之类的东西)到使用DrawUser*函数时的顶点,一应俱全。它还包括任何电话SetDataGetData在纹理,顶点缓冲区等。

在这一点上,我应该说,您可以调用的所有SetData内容(纹理,顶点和索引缓冲区等)以及Effects都保留在GPU内存中。它不会经常重新发送到GPU。引用该数据的绘图命令只需带有指向该数据的指针即可发送。

(另外:您只能从主线程发送绘图命令,但可以SetData在任何线程上。)

XNA复杂的事情有点与它的渲染状态类(BlendStateDepthStencilState,等)。该状态数据每绘图调用发送(每批)。我不是100%肯定,但是给我的印象是它是延迟发送的(它只发送发生变化的状态)。无论哪种方式,相对于批处理成本,状态更改都便宜到免费。

最后,最后要提到的是内部GPU管道。您不想通过写入仍需要读取的数据或读取仍需要写入的数据来强制刷新它。管道刷新意味着它等待操作完成,以便在访问数据时一切都处于一致状态。

需要注意的两个特殊情况是:调用GetData动态对象-特别RenderTarget2D是GPU可能正在写入的对象。这对性能非常不利-请勿这样做。

另一种情况是调用SetData顶点/索引缓冲区。如果您需要经常执行此操作,请使用DynamicVertexBuffer(也DynamicIndexBuffer)。这些使GPU知道它们将经常变化,并在内部进行一些缓冲操作以避免管道刷新。

(还请注意,动态缓冲区比DrawUser*方法快-但必须以所需的最大大小对其进行预分配。)

...这几乎是我所了解的XNA性能的全部信息:)


非常感谢你!这正是我一直在寻找并希望得到的:)。
Aidiakapi 2012年

1
百个批次的每帧听起来过于悲观。我一直听到的经验法则是每帧2K到3K批次。某些游戏在PC上的速度可达10K,但我认为这需要格外小心。
内森·里德

完全正确。“几百”数字来自“批处理批次”论文-列出了“ 25k批处理/ s @ 1GHz CPU的100%”。但是那篇论文已经有十年历史了,从那以后驱动程序和CPU有了很大的进步。我将对此(和我的其他人)进行更新,使其读为“几千”。
安德鲁·罗素
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.