(Direct3D 10) 复制和访问资源数据

不再需要将资源视为在视频内存或系统内存中创建。 或者运行时是否应管理内存。 由于新的 WDDM (Windows 显示驱动程序模型) 的体系结构,应用程序现在创建具有不同 使用 标志的 Direct3D 10 资源,以指示应用程序打算如何使用资源数据。 新的驱动程序模型虚拟化资源使用的内存;然后,操作系统/驱动程序/内存管理器负责将资源置于尽可能高性能的内存区域(考虑到预期的使用情况)。

默认情况下是针对用于 GPU 的资源。 当然,话虽如此,有时资源数据需要提供给 CPU。 复制周围的资源数据,以便适当的处理器可以访问它,而不会影响需要对 API 方法工作方式有所了解的性能。

复制资源数据

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

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

根据创建资源的方式,并非始终可以直接访问基础数据。 这可能意味着,此资源数据必须从源资源复制到其他适当的处理器可以访问的资源。 就 Direct3D 10 而言,GPU 可以直接访问默认资源,动态和过渡资源可由 CPU 直接访问。

创建资源后,无法更改其 用法 。 而是,将一个资源的内容复制到用其他用法创建的另一个资源。 Direct3D 10 通过三种不同的方法提供此功能。 前两种方法 ( ID3D10Device::CopyResourceID3D10Device::CopySubresourceRegion) 旨在将资源数据从一个资源复制到另一个资源。 第三种方法 (ID3D10Device::UpdateSubresource) 旨在将数据从内存复制到资源。

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

复制非可映射资源间的数据非常快速,因为这是最常见的情况,并且经过优化,可以很好地运行。 由于这些资源不可通过 CPU 直接访问,但它们已经过优化,以便 GPU 可以快速操作。

复制可映射资源间的数据问题更多,因为性能将取决于通过其创建资源的用法。 例如,GPU 可以相当迅速地读取动态资源,但不能写入它们,而 GPU 无法直接读取或写入暂存资源。

希望将数据从默认使用的资源复制到具有暂存使用率的资源的应用程序 (允许 CPU 读取数据,即 GPU 读回问题) 必须谨慎执行此操作。 有关最后一个案例的详细信息,请参阅 访问资源数据

访问资源数据

访问资源要求映射资源;映射本质上意味着应用程序正在尝试向 CPU 授予对内存的访问权限。 映射资源(以便 CPU 可以访问基础内存)的过程可能会导致某些性能瓶颈,因此,对于如何和何时执行此任务必须格外小心。

如果应用尝试在错误的时间映射资源,性能可能会陷入停滞状态。 如果应用程序尝试在完成操作前访问操作结果,将发生管道停滞。

在错误的时间映射操作会强制 GPU 与 CPU 互相同步,可能会导致性能严重下降。 如果应用程序想要在 GPU 将资源复制到 CPU 可以映射的资源中之前访问资源,将发生此同步。

CPU 只能从使用 D3D10_USAGE_STAGING 标志创建的资源进行读取。 由于使用此标志创建的资源不能设置为管道的输出,因此如果 CPU 想要读取 GPU 生成的资源中的数据,则必须将数据复制到使用暂存标志创建的资源。 应用程序可以使用 ID3D10Device::CopyResourceID3D10Device::CopySubresourceRegion 方法将一个资源的内容复制到另一个资源。 然后,应用程序可以通过调用相应的 Map 方法获取对此资源的访问权限。 当不再需要访问资源时,应用程序应调用相应的 Unmap 方法。 例如, ID3D10Texture2D::MapID3D10Texture2D::Unmap。 不同的 Map 方法根据输入标志返回一些特定值。 有关详细信息 ,请参阅映射备注部分

注意

当应用程序调用 Map 方法时,它会收到指向要访问的资源数据的指针。 运行时确保指针具有特定的对齐方式,具体取决于 功能级别。 对于 D3D_FEATURE_LEVEL_10_0 及更高版本,指针将对齐到 16 个字节。 对于小于 D3D_FEATURE_LEVEL_10_0,指针将对齐到 4 个字节。 16 字节对齐允许应用程序在本机对数据执行 SSE 优化操作,而无需重新对齐或复制。

 

性能注意事项

最好将电脑视为作为并行体系结构与两种主要处理器类型(一或多个 CPU 和一个或多个 GPU)共同运行的计算机。 与任何并行体系结构一样,当已为每个处理器计划足够的任务以避免其空闲,且一个处理器不会等待另一个处理器工作时,可以实现最佳性能。

对于 GPU/CPU 并行度而言,最坏的情况是需要强制一个处理器等待另一个处理器完成的工作结果。 Direct3D 10 尝试通过使 ID3D10Device::CopyResourceID3D10Device::CopySubresourceRegion 方法异步删除此成本;方法返回时不一定执行复制。 此方法的好处是,应用程序无需支付实际上复制数据的性能成本,直到 CPU 访问数据(调用 Map 时)。 如果在实际复制数据后调用 Map,不会发生性能丢失。 另一方面,如果在复制数据之前调用 Map 方法,会发生管道停滞。

Direct3D 10 (的异步调用是绝大多数方法,尤其是呈现调用) 存储在所谓的命令缓冲区中。 此缓冲区位于图形驱动程序内部,并且用于基础硬件的批量调用,以便在 Microsoft Windows 中尽量少发生从用户模式到内核模式的昂贵切换。

在四种情况中的一种情况下,刷新命令缓冲区,从而导致用户/内核模式切换,如下所示。

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

在上述四种情况中,第四种对性能最关键。 如果应用程序发出 ID3D10Device::CopyResourceID3D10Device::CopySubresourceRegion 调用,则此调用在命令缓冲区中排队。 如果应用程序随后尝试在刷新命令缓冲区之前映射作为复制调用目标的暂存资源,则管道将发生停滞,因为不仅需要执行 Copy 方法调用,而且命令缓冲区中的所有其他缓冲命令也必须执行。 这将导致 GPU 和 CPU 同步,因为 CPU 正在等待访问暂存资源,而 GPU 正在清空命令缓冲区,并将最终填充 CPU 需要的资源。 GPU 完成复制后,CPU 将开始访问暂存资源,但在此期间,GPU 将会处于空闲状态。

在运行时经常执行此操作将会严重降低性能。 因此,应谨慎完成映射使用默认用法创建的资源这一操作。 尝试映射相应的暂存资源之前,此应用程序需要等待足够长的时间,以便清空命令缓冲区并执行完所有命令。 应用程序应等待多久? 至少两帧,因为这需要能够最大化地利用 CPU 和 GPU 之间的并行度。 GPU 的工作原理是,当应用程序通过将调用提交到命令缓冲区处理 N 帧时,GPU 正忙于执行来自上一帧(N-1 帧)的调用。

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

Frame GPU/CPU 状态
N
  • CPU 发出当前帧的渲染调用。
N+1
  • GPU 正在执行 N 帧期间从 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 ...

 

资源 (Direct3D 10)