gdi+ 中发生一般性错误_【译】拆解D3D12和Vulkan中的Barrier(5)

欢迎来到第五部分,上一篇文章中,我们讨论了在我们虚构的GPU上,各种级别的抢占是如何发生的,以及如何改进命令处理器来提高效率,减少延迟。

在这篇文章中,我将讨论真实的GPU,以及它们如何在真实的操作系统和驱动程序中处理高优先级任务。我主要讨论的是Microsoft Windows以及相关联的Windows Display Driver Model (WDDM),以及Windows的原生图形API,也就是D3D11和D3D12,因为这是我最了解的。我也会说到Vulkan,但我并不是这方面的专家。如果你需要有关Linux,MacOS,OpenGL或Metal的信息,那么应该去看看别的文章。我还将讨论台式机GPU,因为这些几乎就是我工作的内容。

本文的第一部分是对Windows 10之前的技术的回顾,如果您不感兴趣,可以跳过。 但是我认为它可以帮助你更多得理解D3D12的设计理念。由于我只是在应用程序方面稍有经验,但对Windows驱动程序模型的理解在某些方面还可能不准确。 如果你发现了某些错误,请随时与我联系,或在评论中纠正我。


THE PAST: XDDM AND WDDM 1.X

当我刚开始学习图形编程的基础知识时,我当时正是在2005/6的大学三年级时。 当时我使用了最新的3D图形API来绘制了blingbling的球体,也就是使用在Windows XP上运行的D3D9。但那时候,GPU上的3D渲染仍然不是主流,一般只有全屏游戏才用得上3D渲染。 理论上可以使用D3D9为两个不同的程序渲染两个不同的窗口,但其兼容程度要视显卡而异。那时候最出名的错误是Lost Devices,也就是从全屏和窗口模式切换时出现的错误。

这是因为Windows 2000 / XP显示驱动器模型(display driver model ,称为XDDM或XPDM)通常使用驱动程序来处理复杂问题,例如为命令缓冲区制定顺序并提交给GPU,或从多个进程上管理GPU资源,当事情涉及到内存或资源管理时就很复杂,因为操作系统和驱动程序需要使用DirectDraw infrastructure,但是你仍然使用旧版GDI管线并带有自己的驱动程序,需要和D3D和DirectDraw互操作。

那时候能处理3D世界的GPU仍然是新奇事物,而2D图形处理器还无处不在。不过随着Windows Vista发布,情况也有了巨大改变。现在即使在低端机器中也可以找到可以处理3D的显卡,这要归功于因特尔的集成GPU。GPU可编程性不断增强,Microsoft里的操作系统开发人员也正在考虑将GPU的3D渲染功能用于桌面上显示UI和核心组件。GPU不断成长,这意味着OS /驱动程序栈应该随着它一起成长。 这就是WDDM的用武之地。

WDDM是“ Windows Display Driver Model”的简称,它是新的驱动程序堆栈,用于替代Vista以前的XDDM / XPDM。 WDDM与之前的模型完全不同,因为它开始将GPU及其内存视为由操作系统管理的共享资源。 例如,使用WDDM,操作系统现在负责将命令缓冲区提交给GPU,而驱动程序仅提供了 hooks 以根据硬件特定格式构造命令缓冲区,然后执行被OS排列好的命令缓冲区。这使操作系统内部程序可以决定在什么时候执行哪个程序的任务,因此可以根据优先级来安排它们。同样,全局内存管理器现在“拥有”了显卡的内存,驱动程序再次提供了必要的 hook,使操作系统可以将数据从系统内存移动到显卡内存,反之亦然。这有助于在所有硬件供应商之间统一GPU内存管理,并通过有效地虚拟化所有资源来消除对各种补丁的需求,例如D3D9中的oldMANAGED资源池。 通过WDDM驱动程序堆栈的一般流命令如下所示:

bc611daaf6044dc047568f6cd7dd2703.png

你可能看不太懂上图,例如“ DMA buffer”到底是什么? 这听起来可能与硬件DMA单元有关,但实际上,这正是OS在“用户模式/内核模式”上的“内核模式”调用命令缓冲区的功能。 我怀疑该名称的“ DMA”来自GPU可以通过物理地址直接从DMA缓冲区读取的事实, 你也可能不太清楚“带有物理地址的DMA缓冲区(Patch DMA Buffer with Physical Addresses)”。 在WDDM 1.0时代,GPU的内存控制非常简单,只能使用物理地址访问其内存。与之相比,CPU虽然使用的是虚拟地址,但可以用页面表(page table)将其映射到物理地址。

由于某些原因,在处理诸如缓冲区或纹理之类的资源时,让GPU供应商提供的驱动程序直接使用物理地址通常是个坏主意,但在WDDM下,操作系统可以在适当的时候把资源移入和移出设备内存。因此WDDM要求驱动程序与DMA缓冲区一起提交allocation和patch列表。【这句我可能翻译得不准,原句WDDM requires that the driver submit allocation and patch lists alongside DMA buffers】。

allocation list告诉操作系统DMA缓冲区内的命令引用了哪些资源,这使内存管理器可以确定哪些资源需要驻留在设备内存中。然后,patch lists会告诉操作系统被引用的资源位于DMA缓冲区中的哪里,然后OS会为 DMA分配正确的物理地址,然后在GPU上执行这些缓冲区。很显然,当一个应用程序提交了一些draw call的时候,GPU上会发生很多事情!但是D3D程序开发者只需要关心上图最左侧一列的事情,而其他的事情不用太关心。虽然GPU由并行处理器组成,并且充满命令流的命令缓冲区将进入这些处理器,但你在使用D3D10/D3D11 API的时候完全不会意识到这一点!你可以认为你的Draw 和 Dispatch 命令能够按顺序执行并且得到正确结果。编写D3D9/10/11的应用程序相对容易一些,而编写操作系统和驱动程序则复杂得多,尤其是在XDDM到WDDM 1.0的过渡时间。但现在WDDM越来越成熟,你可以一边玩大型3D游戏“战地风云1”,一边让Chrome使用GPU加速来渲染网页。

尽管通过各种Windows版本和Service Pack对WDDM进行了许多次更新,但值得一提的是Windows 8的一项重大改进。 从WDDM 1.2开始,驱动程序可以指定是否支持命令缓冲区的抢占,以及采用的粒度级别。 驱动程序通过返回两个枚举值(一个用于图形渲染,另一个用于计算)来表明其是否支持,如下:

typedef 

正如我们在之前的文章中所讨论的那样,多任务处理时,更细粒度的抢占可以带来更好体验。你可以使用旧的DirectX Caps Viewer查看显卡报告的提示粒度! 如今,它已作为Windows 10 SDK的一部分包含在内,因此通常可以在C: Program Files(x86) Windows Kits 10 bin <SDKVersion> x64 dxcapsviewer.exe中找到它。 Nvidia Titan V对图像渲染是像素级别,对计算则是dispatch 级别,而AMD RX Vega对图形渲染是像素级别,对计算是DMA缓冲区级别。 非常感谢Locuza向我指出了这一点!

PROBLEMS WITH D3D11 AND WDDM 1.X

从D3D应用程序开发人员的角度来看,从 WIN XP到 WIN7,情况正在变得越来越好,多任务可以在多GPU正常的执行。虽然程序员们还是经常吐槽这里不好那里不好,但有关性能和多线程的还是有一些非常棒的事。

D3D11首次发布时,大张旗鼓地宣传了它允许通过延迟上下文(deferred contexts)进行多线程渲染的。尽管从技术上讲这是正确的,完全有可能有多个线程发布Draw和Dispatch调用,但实际上效果不大。可能的最大障碍就是我们在第1部分中所讨论:当多个线程生成渲染命令时,如果不考虑线程的依赖关系,那么它们就很难正常工作。

如果你必须遍历一帧的整个命令集,来确定深度缓冲区作为纹理时何时从可写状态转变为可读取,那么就得在生成最终的命令缓冲区前。需要迅速序列化指令集。正因为如此,如果你的目标是使用多个内核来平摊命令缓冲区的产生成本,那么你的目标显然没达到,

使用延迟上下文的原因一般只有一个,那就是希望使用多个内核来平摊命令缓冲区的产生成本。但是如果因为某些原因你必须遍历一帧的所有命令,比如找到深度缓冲区作为纹理时是何时从可写状态转变为可读取的,那么就得在生成最终的命令缓冲区前。需要迅速序列化指令集,这样一来延迟上下文的效果就并不算太好。

另一个问题是D3D11无法在现代GPU上利用多个命令处理器/硬件队列。 正如我们在第3部分中了解到的那样,将命令提交到多个命令处理器可以提高着色器核心利用率。要有效利用这些的命令处理器,就需要识别并提交多个相互不依赖的命令链。 让驱动程序自动执行此操作非常困难,尤其是依赖关系不明显的时候。D3D11 / WDDM 1.x中的命令缓冲区的建立和提交也是隐式的,这使问题变得更加棘手,也就是应用程序无法告诉硬件更多有关命令缓冲区的信息。而且GPU通常会限制命令处理器可以执行的命令种类,比如某些命令处理器只能处理计算命令,但D3D11没有相应的API来让程序开发者做出相应的措施。

最后一个问题和API以及延迟指令集有关。我之前说过,记录和提交命令缓冲区对GPU是隐式的,完全隐藏在驱动程序后面,这样就不用考虑GPU的异步/并行特性,就算draw/dispatch/copy命令同时提交,也可以由驱动程序处理后交给GPU然后得到正确的结果。但是这只是使用即时上下文( immediate context)的情况,延迟上下文则完全不同,因为它并不是立即执行的,只有当调用了ExecuteCommandList时才能执行。

虽然我们把它们称作即时上下文和延迟上下文,但由于隐式提交命令缓冲区也有延迟,所以我们也可以将它们分为“延迟上下文”和“双重延迟上下文”。

由于是面向用户的模型,延迟命令列表也有一些奇怪的限制,比如 Map 只能用于写入而不能读取。这是因为在调用ExecuteCommandList前,命令列表的命令并不能得到真正执行。所以你无法读取之前的命令的结果。这和D3D12的工作方式相似。但更加奇怪的是,这些东西只有在D3D11中使用多线程的时候才适用。我怀疑在多线程的延迟上下文上使用D3D11_MAP_WRITE_DISCARD,背后的自动内存管理和状态转换可能已经一片混乱了。这时候,我们需要一个可以让程序员按照自己的需求管理内存的方法。

THE PRESENT: WINDOWS 10, D3D12, AND WDDM 2.0

借助D3D12和WDDM 2.0,我们可以按照自己需要的方式将命令提交给GPU。 D3D12几乎直接将GPU的细节暴露给了应用程序,而没有忽视把命令记录到命令缓冲区并且并行处理异步执行的这个事实。D3D12给你提供了记录命令的接口(ID3D12GraphicsCommandList),以及用于存放这些命令的内存的接口 (ID3D12CommandAllocator),可以可以执行命令队列的函数(ExecuteCommandLists).。当然能力越大责任越大,因此我们必须知道哪些命令缓冲区正在被读取,这样就不能向它们写入。但这又导致了典型的双缓冲“提交循环”(double-buffered “submission loop”),应用程序在重新使用旧的命令缓冲区之前,必须先等待GPU把上面的命令都执行完。

ac99617e5689b81b23cd4c35baa26b2d.png

更新常量缓冲区时,不再需要MAP_DISCARD,而是必须设计自己的机制来安全访问内存。如果您想从GPU读取某个结果,则最好确保已提交相关的命令缓冲区并等待其完成,然后再尝试在CPU上读取该内存。划分命令目前来说最好的方法就算让每个线程或任务生成自己的命令缓冲区,然后在记录完成命令后将它们作为链提交。然后再结合显式Barrier,以及通过PSO指定管线状态,就得到了全新的API——D3D12。它能真正跨多个CPU内核并且并行生成命令缓冲区。

在第三部分,我们讨论了应用程序提交多个不同依赖链的情况,这通常称为异步计算(asynchronous compute)。如果我们还在使用D3D11的旧模型,也就是依赖和提交是隐式的,那么驱动程序很难找到不相互依赖的dispatch链,并将它放在第二个命令处理器上执行。但在D3D12中,我们可以对GPU说“此命令列表可以单独执行”,这依靠命令队列和fence来完成。命令队列,也就是Command Queues,顾名思义,代表一个队列,可以将一个完整的命令缓冲区提交到其中以在GPU上运行。创建命令队列时,必须指定是哪种命令列表类型:

  1. COPY – 可以执行 CopyResource 命令
  2. COMPUTE – 可以执行 CopyResource 和 Dispatch 命令
  3. DIRECT – 可以执行 CopyResource, Dispatch, 和 Draw 命令

这些命令列表类型对应着GPU上不同的命令处理器。每个命令处理器可以处理某一类型的命令列表,GPU通常具有至少1个能够执行所有命令的图形命令处理器,以及至少1个DMA单元,这些DMA单元可以发布复制命令,这些复制命令已经针对在PCI-e总线上的传输而进行了优化。很多最近的GPU还有一个额外的只能处理计算命令的命令处理器,我们将在之后讨论细节。同样让我们回到第三部分后处理的那个例子:

125303cbd5624949d4a579c9642e3ba4.png

为了提交这样两条依赖链,让它们都能在命令处理器上工作,我们需要做如下的事:

  • 创建一个 DIRECT 命令队列(command queue),我们将其称为 GfxQueue
  • 创建3个DIRECT 命令集( command lists),我们将其称为 GfxCmdListA,GfxCmdListB,GfxCmdListC
  • 创建一个 COMPUTE 命令队列以及命令集,称为ComputeQueue和ComputeCmdList
  • 创建两个 fence,称为 FenceA和FenceB
  • 每一帧:
    • 把所Main pass的渲染指令记录到 GfxCmdListA
    • 把所光晕(bloom)的渲染指令记录到 GfxCmdListB
    • 把所 tone mapping和接下来的渲染指令记录到 GfxCmdListC
    • 把所有 景深(Depth of Field)的dispatch记录到 ComputeCmdList
    • 将 GfxCmdListA提交到 GfxQueue
    • 在 GfxCmdListA 执行完成后,给GfxQueue中的 FenceA赋值,或者叫Signal
    • 将 GfxCmdListB 提交到 GfxQueue
    • 告诉 ComputeCmdList 等到 FenceA 赋值完成
    • 在 ComputeCmdList 执行完成后,给ComputeQueue中的 FenceB赋值
    • 告诉 GfxQueue 等到 FenceB 赋值完成
    • 将 GfxCmdListC 提交到 GfxQueue

f1d8c6a5b7e823905e8dc4380583a358.png

这些D3D12 命令被描述的非常清楚,其间的依赖关系以及应该被提交给哪种队列也非常清楚。因此OS和驱动程序现在具有足够的信息,可以在不同的命令处理器上安排命令列表。这是自D3D11以来的重大改进,因为D3D11的隐式提交模型不可能做到这一点。

根据这些原理,你可能认为在ID3D12CommandQueue上提交了一个命令列表,然后相应的命令缓冲区将被直接提交到GPU的命令处理器的处理队列上去。但是D3D12和WDDM 2.0却并不是这么做的。当我们讨论WDDM 1.x的时候,我们讨论了操作系统用于自己的软件计划表(software scheduler),可以决定一个特定的命令缓冲区什么时候在GPU上运行,并且还能处理多个应用程序的提交。在D3D12 / WDDM2.0中,情况依然如此。因此,并不是将命令缓冲区提交到硬件队列,而是将它们提交到操作系统的调度程序中。除了让操作系统处理多个应用程序之间的共享资源之外,它还允许操作系统“虚拟化”硬件队列,哪怕GPU实际上只有一个命令处理器。你可能已经发现这些命令列表类型其实是包含关系,也就是DIRECT类型可以完成在COMPUTE或COPY队列上可以执行的所有操作,而COMPUTE可以完成在COPY队列上可以执行的所有操作:

c5213b9f72a528cada62498c0e401168.png

这样操作系统就可以根据需要把COPY队列放到可以处理DIRECT的命令处理器上,这样应用程序就不必考虑用户的实际硬件类型和驱动程序支持的功能。但是,要实现这一点,操作系统必须能够获取原本打算并发的命令缓冲区,并将其“压缩”到一系列命令缓冲区流中。 举例来说,让我们返回之前使用的DOF / bloom案例。 如果我们要在只有单个DIRECT引擎的GPU上提交命令缓冲区序列,则OS必须将提交内容展平为以下形式:

4120678a41c45fd374cec48be473abb8.png

由于Bloom和DOF命令缓冲区彼此独立,没有依赖关系,因此我们先执行哪个都可以。但是我们应当注意,操作系统需要完全了解队列间的依赖关系,才能执行这种扁平化操作,这就是为什么Fence 是比 Barrier更加重要的同步操作。这也是为什么无法在命令列表中执行跨队列同步的原因,这样虽然应用程序编写起来更加方便,但会破坏操作系统展平多队列的能力。通过让操作系统提前知道这些全局依赖关系,可以预防某些死锁。

如果您想知道操作系统和调度程序在后台使用命令缓冲区的实际工作,最好的方法是使用GPUView,使用Bruce Dawson的强大工具UIforETW tool工具更容易一些 。 这是使用的是AMD RX 460,向DIRECT和COMPUTE队列提交大量工作的捕获示例:

5e4815b1d29201fddf6baeffa96e0248.png

该屏幕快照显示了大约4帧的捕获数据,看看flip queue就知道了。较暗的区域显示了在等待同步时GPU的空闲,因此我们可以使用它来表示帧的开始和结束位置。

在底部的“Device Context”,表示的是单个进程已经把命令缓冲区提交到GPU端,上面那个Device Context显示的是compute提交,下面的Device Context显示的是graphics/direct提交。

在上图的最上面,所有的命令缓冲区都在硬件队列上。我们看到有两个硬件队列,一个称为“ 3D”,另一个称为“ COMPUTE_0”。我们可以看到它们都同时处理提交,这表明每个队列中的工作将会重叠。我们在这里看到的内容与我在第4部分说的非常相似,这意味着我们还可以使用此工具来检查多个应用程序的抢占行为。

而Vulkan与D3D12有所不同。在Vulkan中,可以查询每个物理设备以找到其支持的队列“families”,类似于D3D12中的队列类型,以及每个队列 families 中有多少可用。 然后,您可以在创建逻辑设备时将句柄传递给那些暴露出来的队列,然后允许您在运行时将命令缓冲区提交给那些队列。 这意味着,如果GPU仅具有一个命令处理器,则您可能只会在物理设备上看到一个队列。

然后是WDDM 2.0的另一项改进:还记得WDDM 1.x需要命令缓冲区指定物理地址的过程吗?由于WDDM 2.0增加了对GPU虚拟内存的支持,因此在WDDM 2.0中不再需要此功能。使用新的驱动程序模型,每个进程都会在GPU内存上获得自己的虚拟地址空间,就像对CPU内存一样。应用程序和驱动程序可以将这些虚拟地址直接嵌入命令缓冲区,或ExecuteIndirect argument buffers,而无需在执行之前进行昂贵的patching过程。WDDM 2.0还允许用户模式下的驱动程序直接构建并提交可访问GPU的命令缓冲区,而不需要内核模式下的驱动程序创建DMA缓冲区。这样在性能和安全性上都有所提高。

使用D3D12 API时,通常会直接使用GPU虚拟地址来处理一些事情,诸如创建顶点缓冲区视图,或使用单个指针设置根CBV / SRV的操作,这对于应用程序来说非常方便。另一方面,Vulkan不会暴露GPU虚拟地址,这可能是因为它需要支持跨平台,而有些平台没有完整的虚拟地址支持,所以代替品就是allocation/offset pairs。

A QUICK LOOK AT REAL GPU’S

在结束本文之前,我还想花一点时间研究一下真实世界中的GPU,及其对多个命令处理器的支持。 我们首先看看AMD的GPU。这是来自 AMD’s presentations 的一张幻灯片。

4c366ebc91cda15506954c77bc7511fc.png

在此图中,有一个“ GFX”命令处理器,它是唯一能够发布使用图元和像素管线的Draw命令的命令处理器。它还可以发布Dispatch call,让CS管线访问着色器核心。左上角有几个标有“ ACE”的单元,代表“异步计算引擎(Asynchronous Compute Engine)”,是一组简化的独立命令处理器,只能处理与计算相关的命令。这意味着他们可以发出Dispatch call并执行与同步相关的命令,但不能执行利用了图形管线的Draw命令。换句话说,它们完全符合D3D12中COMPUTE引擎所需的功能集,这显然不是巧合。在功能方面,它们也与我的虚构MJP-4000的双命令处理器非常相似,它们可以支持抢占,多任务处理和重叠多个工作负载,这样就能提高着色器核心利用率。如果您阅读了一些AMD的文档,你会发现它所说的和我之前解释的一些概念相同。

实际上,ACE比我的示例中讨论的要复杂一些,特别是由于每个ACE最多包含8个硬件队列,用于将命令缓冲区送到ACE的命令处理器。 AMD的处理器在单个芯片上最多支持8个ACE,从而有可能同时运行64个不同的命令流!单独的队列支持各种调度和同步操作,这样就可以把它们作为一个简单的硬件任务处理器。这些ACE在GCN架构的每次更新中都存在,包括最近的RX Vega系列【译注:原博客写于2018年9月,下同】。 AMD还提到了在4种体系结构中的一些改进。最著名的是Polaris架构,ACE实现了“Quick Response Queue”而变强了。他们的文档说这使ACE可以提交高优先级的工作,这些工作优先于其他命令处理器提交的工作,这应该就是我所说的线程级抢占。

所以我们应该看到至少有1个计算引擎暴露给Windows调度程序,以及Vulkan上至少1个在物理设备上暴露了COMPUTE_BIT队列。 可以通过使用ETW captures和GPUView通过实验来验证D3D12端,请参见上面的图片,其中显示了在硬件计算队列上执行COMPUTE提交的情况。但是对于Vulkan,我们可以使用Vulkan硬件数据库轻松地进行验证。 这是在查看RX Vega的“Queue families”选项卡时得到的结果:

5b7ca1e4ba31c3940d028a6a1afc7f6f.png

看来AMD选择将ACE作为8个计算队列暴露给Vulkan,对于单个应用程序来说足够了。我们还可以看到指定了2个TRANSFER_BIT的队列,这对应D3D12上的COPY队列。这些对应于AMD Vega以及几乎所有普通的独立显卡上的硬件DMA单元。就像我之前提到的那样,GPU上的DMA单元经过专门优化,可通过PCI-e总线将批数据传输载GPU内存中。典型的用例是只读纹理和缓冲区,它们需要停留在GPU内存中,以便GPU能够以非常快地读取。在没有D3D12的时候,驱动程序将使用DMA和应用程序指定的内存来初始化资源,这是因为DMA单元针对系统内存的传输进行了优化,并且它可以与图形操作同时执行。 DMA单元还可以将纹理格式针对具体的硬件进行转换,这通常涉及使用Z阶曲线的某些变体以获得更好的局部性2D缓存。使用D3D12和Vulkan,你可以自行初始化资源,而在独立显卡上使用COPY队列则是最自然的选择。

Nvidia的显卡似乎还允许同时向GPU提交多个工作负载。 他们的开普勒架构引入了Hyper-Q,这听起来与AMD GPU上的ACE非常相似:

663b82d4609a55e8a40b845b45e3e6a7.png

这些稀少的文档描述了32个硬件队列,并提到它们可用于从同一应用程序提交多个内核,或允许多个CUDA应用程序同时提交工作负载。不幸的是,似乎此功能仅限于CUDA,因为Nvidia的评论说他们的GPU无法同时执行Hyper-Q命令和图形命令。根据Nvidia的说法,只有他们最新的Maxwell 2.0架构才能在“混合模式”下运行,其中1个硬件队列可以处理图形命令,而其他31个队列可以同时处理计算命令。这和AMD在理论上是一样,但是看来Nvidia从未将其他计算队列暴露给D3D12或Vulkan,而是允许操作系统将计算提交合并到图形队列中。直到Nvidia发布了他们最新的Pascal架构后,他们才决定最终提供有关该主题的一些信息。他们的白皮书中有一个部分描述了他们称为“动态负载平衡(dynamic load balancing)”的新功能,其中包含以下图表和第14页的文字:

b9c7693306200f177eded861d6df54f8.png

“In Maxwell generation GPUs, overlapping workloads were implemented with static partitioning of the GPU into a subset that runs graphics, and a subset that runs compute. This is efficient provided that the balance of work between the two loads roughly matches the partitioning ratio. However, if the compute workload takes longer than the graphics workload, and both need to complete before new work can be done, and the portion of the GPU configured to run graphics will go idle. This can cause reduced performance that may exceed any performance benefit that would have been provided from running the workloads overlapped.”

这表明问题不在他们的队列中,而可能在执行计算着色器的实际功能单元中。 它们的描述听起来好像它们的硬件单元需要被指定为“图形”或“计算”模式,这意味着如果要使计算与图形重叠,需要对硬件进行静态分区。这显然并不太好,这也是为什么最好将计算队列提交序列化为图形提交更好。 同时,Pascal架构听起来好像可以动态重新分区,这将使并发计算执行更加可行。 我们可以在GPUView中查看Pascal GPU在计算硬件队列上执行COMPUTE提交,或者通过查看Vulkan中暴露的队列来确认这一点:

29ad24e1049ff9357bac43df9db54f52.png

我们在这里看到8个计算队列,它们与RX Vega的描述相匹配。 有趣的是,我们还看到16个GRAPHICS_BIT的队列,这也很有趣!但是Nvidia没有透露更多的信息。Pascal白皮书的后面还会有一些页面,提供有关其线程级抢占(thread-level preemption)的一些详细信息,这是对早期Maxwell 2.0架构所支持的绘制级抢占(draw-level preemption)的重大改进。 有趣的是,它们的硬件似乎还具有指令级抢占(instruction-level preemption)功能,但仅适用于CUDA。 尚未发布的Volta或Turing架构可能还有一些改变。

在Skylake CPU中使用的Intel Gen9架构似乎并没有向D3D12或Vulkan暴露单个DIRECT/graphics命令处理器。 GPUView仅显示1个用于处理命令的硬件队列,而Vulkan DB仅显示1个在物理设备上暴露的队列。我拿不到9.5代GPU(在Kaby Lake上使用),因此我无法亲自对其进行测试。 但是,9.5代这些GPU似乎仍然只在Vulkan中暴露一个队列,因此我认为在这方面其实没有变化。 这些GPU上没有COPY命令处理器可能很奇怪,但是请记住DMA单元主要是独立显卡的功能。 像Intel一样的集成显卡利用CPU内存而不是专用显存,这意味着CPU可以写入该内存,而不需要专用硬件单元。

NEXT UP

在本系列的第六部分(也是最后一部分!)中,我将分享一些我对重叠的GPU命令和GPU抢占进行试验所获得的结果。 敬请关注!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值