为什么绘制电话昂贵?

假设纹理,顶点和着色器数据已经在显卡上,则不需要向卡发送太多的数据。 有几个字节来标识数据,大概是一个4×4的matrix,还有一些其他的参数。

那么所有的开销从哪里来? 这些操作是否需要与gpu进行某种握手?

为什么发送一个包含一堆在CPU上计算的小模型的网格通常比发送顶点ID和变换matrix要快? (第二个选项看起来应该有更less的数据发送,除非模型小于4x4matrix)

首先,我假定“绘制调用”是指告诉GPU将某个顶点集合呈现为具有特定状态(着色器,混合状态等)的三angular形的命令。

画电话不一定很贵。 在较旧版本的Direct3D中,许多调用需要一个上下文切换,这很昂贵,但在新版本中并不是这样。

减less绘制调用的主要原因是graphics硬件可以转换和渲染三angular形比提交它们要快得多。 如果每次调用都会提交几个三angular形,那么您将完全被CPU绑定,并且GPU将大部分空闲。 CPU将无法足够快地提供GPU。

使用两个三angular形进行单个绘制调用是便宜的,但是如果每次调用提交的数据太less,则不会有足够的CPU时间向GPU提交尽可能多的几何graphics。

在进行绘制调用时有一些实际成本,它需要设置一堆状态(使用哪一组顶点,使用哪种着色器等),并且状态更改在硬件方面都有成本(更新一堆的寄存器)和驾驶员侧(validation和翻译你的呼叫设置状态)。

但是,只有在每个调用提交的数据太less的情况下,绘制调用的主要成本才会适用 ,因为这将导致您受到CPU限制,并阻止您充分利用硬件。

就像Josh说的那样,绘制调用也会导致命令缓冲区被刷新,但是在我调用SwapBuffers时经常会发生这种情况,而不是在提交几何时。 video驱动程序通常会尝试尽可能多地缓冲(有时候需要几帧),以便尽可能多地从GPU中平行排列。

您应该阅读nVidia演示文稿批量批量批次! ,这是相当古老的,但涵盖了这个话题。

Direct3D等graphicsAPI将其API级调用转换为与设备无关的命令,并将它们排列在缓冲区中。 冲洗该缓冲区以执行实际工作是昂贵的 – 因为这意味着正在执行实际工作,并且因为它可能导致从芯片上的用户切换到内核模式(并且返回),这不是低廉。

在刷新缓冲区之前,只要CPU没有发出阻塞请求(例如将数据映射回CPU),GPU就可以与CPU并行做一些准备工作。 但GPU不会 – 也不能 – 准备好所有东西,直到需要真正绘制。 仅仅因为卡上的某些顶点或纹理数据并不意味着它已经被恰当地安排了,并且在设置顶点布局或着色器被绑定之前可能是不可布置的,等等。 实际工作的大部分发生在命令刷新和绘制调用期间。

DirectX SDK有一个关于精确分析D3D性能的部分,虽然与你的问题没有直接关系,但它可以提供一些提示,指出什么是和不是很昂贵,以及(在某些情况下)为什么。

这个博客post (以及后续的post)与这个和这里的后续post更相关,它们提供了GPU的逻辑,低级操作过程的一个很好的概述。

但是,本质上(尝试直接回答你的问题),电话费用昂贵的原因并不是必须传输大量数据,而是除了通过总线传输数据之外还有大量的工作这被推迟到命令缓冲区被刷新。

简短的回答:驾驶员缓冲部分或全部的实际工作,直到您调用绘图。 这将显示为一个相对可预测的时间花在绘图调用,取决于多less状态已经改变。

这是由于几个原因:

  • 避免做不必要的工作:如果在绘制之前(不必要地)多次设置相同的状态,可以避免在每次出现这种情况时花费大量的工作。 这实际上在一个大型的代码库中变成了一个相当普遍的事件,比如一个生产游戏引擎。
  • 能够调和内部相互依赖的国家,而不是立即用不完整的信息来处理它们

备选答案:

  • 驱动程序用来存储渲染命令的缓冲区已满,应用程序正在等待GPU处理一些早期的工作。 这通常会在帧内的随机抽取调用中显示为非常大的时间块阻塞。
  • 驱动程序允许缓冲的帧数已经达到,应用程序正在GPU上等待处理其中的一个。 这通常会在帧的第一次绘制调用中显示为大块时间阻塞,或者在前一帧结束时显示为存在。