DirectX 11中的渲染

4
当帧开始时,我会进行逻辑更新和渲染。在我的渲染代码中,我做一些常规的事情。我设置一些状态、缓冲区、纹理,并最终调用Draw方法。
m_deviceContext->Draw(
        nbVertices,
        0);

在帧结束时,我调用 present 来显示渲染的帧。
// 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,而主线程(调用Draw的那个)则继续执行?还是它会等待渲染完成?

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


1
绘制提交(并继续),Present 实际上是屏幕更新的地方。当启用 VSync 时,您可以看到它会延迟。 - CodeAngry
@CodeAngry 谢谢您。只是为了确保我理解得正确。我调用Draw,数据被发送到GPU,GPU开始计算,我调用Present,屏幕会在GPU完成计算后立即更新。这是正确的吗? - DannyX
1个回答

6
有许多调用可以触发GPU开始工作,其中之一是Draw。其他的包括DispatchCopyResource等。MSDN文档试图说明的是,像PSSetShaderIASetPrimitiveTopology等这样的东西在你调用Draw之前并没有实际作用。

当你调用Present时,它被视为“帧结束”的隐式指示,但你的程序通常可以在第一帧完成和显示之前继续设置下一帧的渲染调用。默认情况下,Windows允许您在阻塞CPU线程之前排队最多3帧的Present调用,以便GPU追赶——在实时渲染中,通常不希望输入和渲染之间的延迟非常高。

事实是,GPU/CPU同步非常复杂,Direct3D运行时也会批处理请求以最小化内核调用开销,因此实际工作可能发生在提交多个Draws到命令队列后。这篇旧文章让你了解如何工作的味道。在现代GPU上,您还可以进行各种内存操作,以分页内存、设置物理视频内存区域等。
顺便说一下,所有这些“魔法”都不存在于Direct3D 12中,但这意味着应用程序必须在“正确”的时间做所有事情,以确保它既高效又功能齐全。程序员直接构建命令队列,触发各种像素和计算GPU引擎上的工作,并执行Direct3D 11运行时自动处理的所有混乱的东西。即使如此,最终视频驱动程序实际上也在与硬件通信,因此他们也可以做其他类型的优化。
这里需要记住的一般规则:
创建资源是昂贵的,特别是运行时着色器编译(由HLSL编译器)和运行时着色器块优化(由驱动程序)。
将资源复制到GPU(即从CPU内存加载纹理数据)需要总线带宽,供应量有限:最好将纹理、VB和IB数据保存在您重用的静态缓冲区中。
从GPU复制资源(即将GPU内存移动到CPU内存)使用比访问GPU更慢的反向通道:尽量避免需要从GPU读取。
每个Draw调用提交更大的几何体块有助于分摊开销(即为10,000个三角形调用一次具有相同状态/着色器的绘图比调用10次为1000个三角形每个之间更改状态/着色器快得多)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接