在 DirectX 11 中渲染

Rendering in DirectX 11

帧开始时,我进行逻辑更新并在之后进行渲染。 在我的渲染代码中,我做了一些平常的事情。我设置了一些状态、缓冲区、纹理,并通过调用 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 submits work to the rendering pipeline.

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

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

有多个调用可以触发GPU开始工作,Draw是一个。其他的包括 DispatchCopyResource 等。MSDN 文档试图说明的是像 PSSetShader 这样的东西。 IASetPrimitiveTopology 等在您调用 Draw.

之前不会真正执行任何操作

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

然而,事实是 GPU/CPU 同步很复杂,而且 Direct3D 运行时也在处理请求以最小化内核调用开销,因此在将许多绘图提交给命令后实际工作可能会很顺利-队列。 This old article 让您了解其工作原理。在现代GPU上,还可以进行内存分页、设置物理显存区域等各种内存操作

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

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

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