2015-06-24 96 views
2

帧开始时,我做了逻辑更新并在此之后渲染。 在我的渲染代码中,我会做一些常用的东西。我设置了几个状态,缓冲区,纹理,并通过调用Draw来结束。在DirectX中渲染11

m_deviceContext->Draw(
     nbVertices, 
     0); 

在框架端我称本显示渲染帧。

// Present the back buffer to the screen since rendering is complete. 
if(m_vsync_enabled) 
{ 
    // Lock to screen refresh rate. 
    m_swapChain->Present(1, 0); 
} 
else 
{ 
    // Present as fast as possible. 
    m_swapChain->Present(0, 0); 
} 

通常的东西。现在,当我调用Draw时,根据MSDN

Draw将工作提交给渲染管道。

这是否意味着数据发送到GPU和主线程(称为绘制)继续?还是等待渲染完成?

在我看来,只有Present函数应该让主线程等待渲染完成。

+0

绘制提交(并继续),实际上是屏幕更新的地方。您可以在启用VSync的情况下查看它,因为它会延迟。 – CodeAngry

+0

@CodeAngry 谢谢。 只是为了确保我理解正确。我称之为Draw,数据发送到GPU,GPU开始计算,我称之为Present,当GPU完成计算后屏幕即会更新。那是对的吗? – DannyX

回答

3

有很多可以触发GPU开始工作的调用,Draw就是其中之一。其他的包括Dispatch,CopyResource等MSDN文档试图说的是像PSSetShader这样的东西。 IASetPrimitiveTopology等在您拨打Draw之前不会做任何事情。

当您调用Present时,将其视为'帧结束'的隐式指示符,但您的程序通常可以继续在第一帧完成并显示之前为下一帧设置渲染调用。默认情况下,Windows将允许您排队3帧,然后阻止Present调用中的CPU线程以让GPU赶上 - 实时渲染时,您通常不希望输入和渲染之间的延迟为真的很高。

但事实是,GPU/CPU同步很复杂,并且Direct3D运行时也在分解请求以最小化内核调用开销,因此在将许多Draw提交到命令队列后,实际工作可能会发生抖动。 This old article给你如何工作的味道。在现代GPU上,您还可以进行各种内存操作,用于在内存中分页,设置物理视频内存区域等。

顺便说一句,所有这些'魔术'在Direct3D 12中都不存在,但这意味着应用程序必须在“正确”的时间做所有事情,以确保它既高效又实用。程序员更直接地构建命令队列,触发各种像素和计算GPU引擎的工作,以及处理所有杂乱的东西,这些东西通过Direct3 11的运行时自动处理得更加抽象。即使如此,最终视频驱动程序是实际与硬件对话的驱动程序,因此它们也可以进行其他类型的优化。

拇指这里的一般规则要牢记:

  • 创建资源是昂贵的,尤其是运行时着色器编译(由HLSL编译器)和运行着色斑点优化(通过驱动程序)
  • 复制资源GPU(即从CPU内存中加载纹理数据)需要总线带宽在供应方面受到限制:优先将纹理,VB和IB数据保留在您可重复使用的静态缓冲区中。
  • 从GPU复制资源(即,将GPU内存移动到CPU内存)使用比去GPU更慢的反向通道:尽量避免需要从GPU回读
  • 每个绘图调用提交更大的几何块有助于分摊开销(例如调用draw for具有相同状态/着色器的10,000个三角形比调用绘制10次更快,每个1000个三角形具有不同状态/着色器之间的变化)。