在 DirectX 11 中渲染

Dan*_*nyX 3 directx-11

当帧开始时,我会进行逻辑更新并在此之后进行渲染。在我的渲染代码中,我做通常的事情。我通过调用 Draw 设置了几个状态、缓冲、纹理和结束。

m_deviceContext->Draw(
        nbVertices,
        0);
Run Code Online (Sandbox Code Playgroud)

在帧结束时,我调用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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

这是否意味着数据被发送到 GPU 并且主线程(称为 Draw 的线程)继续?还是等待渲染完成?

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

Chu*_*urn 5

有许多调用可以触发 GPU 开始工作,Draw其中之一。其他的包括DispatchCopyResource等。 MSDN 文档试图说的是像PSSetShader. IASetPrimitiveTopology等在您调用 之前不会真正执行任何操作Draw

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

然而,事实是,GPU/CPU 同步很复杂,而且 Direct3D 运行时也在增加请求以最小化内核调用开销,因此在许多 Draw 提交到命令队列后,实际工作可能会发生。这篇旧文章让您了解这是如何工作的。在现代 GPU 上,您还可以进行各种内存操作,用于在内存中进行分页、设置物理视频内存区域等。

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

这里要记住的一般经验法则:

  • 创建资源很昂贵,尤其是运行时着色器编译(由 HLSL 编译器)和运行时着色器 blob 优化(由驱动程序)
  • 将资源复制到 GPU(即从 CPU 内存加载纹理数据)需要有限的总线带宽:最好将纹理、VB 和 IB 数据保存在您重用的静态缓冲区中。
  • 从 GPU 复制资源(即将 GPU 内存移动到 CPU 内存)使用比进入 GPU 慢的反向通道:尽量避免需要从 GPU 回读
  • 每次 Draw 调用提交更大的几何块有助于分摊开销(即,为 10,000 个具有相同状态/着色器的三角形调用一次 draw 比为 1000 个三角形调用 draw 10 次要快得多,每个三角形之间的状态/着色器都在变化)。