复制和访问资源数据

使用标志指示应用程序打算如何使用资源数据,将资源放置在可能最高性能的内存区域中。 资源数据跨资源复制,以便 CPU 或 GPU 可以访问它,而不会影响性能。

不必将资源视为在视频内存或系统内存中创建,也不需要确定运行时是否应管理内存。 借助 WDDM(Windows 显示驱动程序模型)的体系结构,应用程序创建具有不同使用标志的 Direct3D 资源,以指示应用程序打算如何使用资源数据。 此驱动程序模型虚拟化资源使用的内存;根据预期使用量,作系统/驱动程序/内存管理器负责将资源放置在可能性能最高的内存区域中。

默认情况是可供 GPU 使用的资源。 有时,资源数据需要提供给 CPU。 复制资源数据,以便适当的处理器可以访问它,而不会影响性能,需要了解 API 方法的工作方式。

复制资源数据

当 Direct3D 执行 Create 调用时,将在内存中创建资源。 可以在视频内存、系统内存或任何其他类型的内存中创建它们。 由于 WDDM 驱动程序模型虚拟化了此内存,因此应用程序不再需要跟踪在其中创建的内存资源类型。

理想情况下,所有资源都位于视频内存中,以便 GPU 可以立即访问它们。 但是,有时需要 CPU 读取资源数据,或者 GPU 访问 CPU 已写入的资源数据。 Direct3D 通过请求应用程序指定使用情况来处理这些不同的方案,然后在必要时提供几种方法用于复制资源数据。

根据资源的创建方式,并不总是可以直接访问基础数据。 这可能意味着必须将资源数据从源资源复制到由相应处理器访问的另一个资源。 就 Direct3D 而言,GPU 可以直接访问默认资源,CPU 可以直接访问动态和暂存资源。

创建资源后,无法更改其使用情况。 相反,将一个资源的内容复制到使用其他用法创建的另一个资源。 将资源数据从一个资源复制到另一个资源,或将数据从内存复制到资源。

有两种主要资源:可映射和不可映射。 使用动态或暂存使用情况创建的资源是可映射的,而使用默认或不可变用法创建的资源不可映射。

在非映射资源之间复制数据速度非常快,因为这是最常见的情况,并且已针对高效性能进行了优化。 由于 CPU 无法直接访问这些资源,因此它们被优化为 GPU 可以快速处理它们。

在可映射资源之间复制数据会比较有问题,因为性能将取决于资源创建的使用情况。 例如,GPU 可以相当快地读取动态资源,但无法写入动态资源,GPU 无法直接读取或写入过渡资源。

希望将数据从默认使用情况的资源复制到暂存使用情况的资源(以允许 CPU 读取数据,即解决 GPU 读回问题)的应用程序必须小心操作。 请参阅下面的章节 :访问资源数据

访问资源数据

访问资源需要映射资源;映射实质上意味着应用程序正在尝试授予对内存的 CPU 访问权限。 映射资源的过程,以便 CPU 可以访问基础内存可能会导致一些性能瓶颈,因此,必须注意如何以及何时执行此任务。

如果应用程序尝试在错误时间映射资源,性能可能会停止。 如果应用程序尝试在该操作完成之前访问操作的结果,将发生管道阻塞。

在错误的时机执行映射操作可能会导致性能严重下降,因为这会强制 GPU 和 CPU 进行同步。 如果应用程序希望在 GPU 完成将其复制到 CPU 可以映射的资源之前访问资源,则会发生此同步。

性能注意事项

最好将电脑视为作为并行体系结构运行的计算机,其中包含两种主要类型的处理器:一个或多个 CPU 和一个或多个 GPU。 与在任何并行体系结构中一样,当每个处理器都计划有足够任务以防止其处于空闲状态,并且一个处理器的工作没有等待另一个处理器的工作时,可以实现最佳性能。

GPU/CPU 并行度最差的情况是需要强制一个处理器等待另一个处理器完成的工作结果。 Direct3D 通过将复制方法设为异步来消除这种成本。复制可能不会在方法返回时立即执行。

这样做的好处是,应用程序不会支付实际复制数据的性能成本,直到 CPU 访问数据(即调用 Map 时)。 如果在实际复制数据后调用 Map 方法,则不会发生性能损失。 另一方面,如果在数据复制之前调用 Map 方法,则会发生管道停滞。

Direct3D 中的异步调用(这是绝大多数方法,尤其是呈现调用)存储在所谓的 命令缓冲区中。 此缓冲区位于图形驱动程序内部,用于批处理对基础硬件的调用,以便在 Microsoft Windows 中尽可能减少从用户模式切换到内核模式的过程。

命令缓冲区已刷新,因此在以下四种情况下会导致用户/内核模式的切换。

  1. 调用 Present。
  2. Flush 被调用。
  3. 命令缓冲区已满;其大小是动态的,由作系统和图形驱动程序控制。
  4. CPU 需要访问等待在命令缓冲区中执行的命令的结果。

在上述四种情况中,第四种情况对性能影响最关键。 如果应用程序发出复制资源或子资源的调用,则此调用将排队在命令缓冲区中。

如果应用程序随后尝试在命令缓冲区被刷新之前映射作为复制调用目标的暂存资源,将发生管道停顿,因为不仅需要执行 Copy 方法调用,而且命令缓冲区中的所有其他缓冲命令也必须执行。 这将导致 GPU 和 CPU 同步,因为 CPU 将等待访问过渡资源,而 GPU 正在清空命令缓冲区,最后填充 CPU 所需的资源。 GPU 完成复制后,CPU 将开始访问暂存资源,但在此期间,GPU 将处于空闲状态。

在运行时频繁执行此作会严重降低性能。 因此,应谨慎地映射那些使用默认设置创建的资源。 应用程序需要等待足够长的时间才能清空命令缓冲区,从而让所有这些命令在尝试映射相应的暂存资源之前完成执行。

应用程序应等待多长时间? 至少两个帧,因为这会使 CPU(s) 和 GPU 之间的并行度得到最大利用。 GPU 的工作方式是,当应用程序通过向命令缓冲区提交调用来处理帧 N 时,GPU 正忙于执行上一帧 N-1 的调用。

因此,如果应用程序想要映射源自视频内存的资源并在帧 N 复制资源,则当应用程序正在提交下一帧的调用时,此调用实际上将在帧 N+1 处开始执行。 当应用程序处理帧 N+2 时,应完成复制。

框架 GPU/CPU 状态
N
  • CPU 问题影响当前帧的渲染调用。
N+1
  • 在帧 N 期间,GPU 执行从 CPU 发送来的调用。
  • CPU 问题影响当前帧的渲染调用。
N+2
  • GPU 已完成在帧 N 期间由 CPU 发送的调用。结果已准备好。
  • GPU 在帧 N+1 期间执行 CPU 发出的调用。
  • CPU 问题影响当前帧的渲染调用。
N+3
  • GPU 在处理帧 N+1 时已完成由 CPU 发送的调用。 结果已出。
  • GPU 在帧 N+2 期间执行由 CPU 发送的调用。
  • CPU 问题影响当前帧的渲染调用。
N+4 ...

 

资源