Vulkan中的队列家庭实际上是什么?

Mak*_*gan 7 c++ hardware gpu vulkan

我目前正在学习vulkan,现在我只是分解每个命令并检查结构以尝试理解它们的含义。

现在,我正在分析QueueFamilies,为此我有以下代码:

vector<vk::QueueFamilyProperties> queue_families = device.getQueueFamilyProperties();
for(auto &q_family : queue_families)
{
    cout << "Queue number: "  + to_string(q_family.queueCount) << endl;
    cout << "Queue flags: " + to_string(q_family.queueFlags) << endl;
}
Run Code Online (Sandbox Code Playgroud)

这将产生以下输出:

Queue number: 16
Queue flags: {Graphics | Compute | Transfer | SparseBinding}
Queue number: 1
Queue flags: {Transfer}
Queue number: 8
Queue flags: {Compute}
Run Code Online (Sandbox Code Playgroud)

所以,我天真地这样理解:

有3个队列族,一个队列族有16个队列,所有队列都可以进行图形,计算,传输和稀疏绑定操作(不知道最后两个是什么)

另一个有1个队列,只能传输(无论是什么)

最后一个有8个能够进行计算操作的队列。

每个队列家庭是什么?我知道这是我们发送绘图和交换缓冲区之类的执行命令的地方,但这是一个较宽泛的解释,我希望获得更多知识的详细答案。

2个额外的标志是什么?转移和稀疏出价?

最后,为什么我们要/需要多个命令队列?

Jes*_*all 12

要了解队列族,您首先必须了解队列。

队列是您向其提交命令缓冲区的对象,并且提交给队列的命令缓冲区是相对于彼此以order [* 1]执行的。提交给不同队列的命令缓冲区彼此之间是无序的,除非您明确地将它们与同步VkSemaphore。您一次只能将工作从一个线程提交到队列,但是不同的线程可以同时将工作提交到不同的队列。

每个队列只能执行某些类型的操作。图形队列可以运行由vkCmdDraw*命令启动的图形管道。计算队列可以运行以开头的计算管道vkCmdDispatch*。传输队列可以从执行传输(复制)操作vkCmdCopy*。稀疏绑定队列可以更改稀疏资源到内存的绑定vkQueueBindSparse(请注意,这是直接提交给队列的操作,而不是命令缓冲区中的命令)。一些队列可以执行多种操作。在规范中,可以提交到队列的每个命令都有一个“命令属性”表,该表列出了可以执行该命令的队列类型。

队列族仅描述一组具有相同属性的队列。因此,在您的示例中,设备支持三种队列:

  • 一种可以执行图形,计算,传输和稀疏绑定操作,并且您最多可以创建16个该类型的队列。

  • 另一种只能执行传输操作,并且您只能创建一个这种队列。通常,这是用于离散GPU上主机与设备内存之间的DMA异步数据传输,因此可以与独立的图形/计算操作同时进行传输。

  • 最后,您最多可以创建8个仅能进行计算操作的队列。

一些队列可能仅对应于主机端调度程序中的单独队列,而其他队列则可能对应于硬件中的实际独立队列。例如,许多GPU仅具有一个硬件图形队列,因此,即使从具有图形功能的队列家族中创建两个VkQueue,提交给这些队列的命令缓冲区也将独立地通过内核驱动程序的命令缓冲区调度程序进行处理,但将以某种串行方式执行在GPU上订购。但是某些GPU具有多个仅用于计算的硬件队列,因此,仅用于计算的队列系列的两个VkQueue实际上可能在GPU中一直独立且同时进行。Vulkan没有公开这一点。

最重要的是,根据您拥有多少并发性来决定可以有效使用多少个队列。对于许多应用程序,它们只需要一个“通用”队列。更高级的服务器可能具有一个图形+计算队列,一个用于异步计算工作的单独的仅计算队列以及一个用于异步DMA的传输队列。然后将您想要的内容映射到可用的内容上;您可能需要进行自己的多路复用,例如,在不具有仅计算队列系列的设备上,您可能会创建多个图形+计算队列,或者将异步计算作业序列化到单个图形+计算队列中。

[* 1]有点简化。它们按顺序开始,但之后可以独立进行并按顺序完成。但是,不能保证不同队列的独立进度。对于这个问题,我将保留它。

  • “您一次只能将工作从一个线程提交到队列”我的理解是,2 个线程可以将命令提交到同一个队列,但它需要同步,以便一次只有一个线程执行。那是对的吗? (2认同)
  • 恩,那就对了。Vulkan 称之为“外部同步”,很多对象都是这样进行外部同步的,也就是说不能有两个线程同时对对象进行操作(除非都是只读操作)。 (2认同)
  • 鉴于 OP 发布的示例(16 个通用队列(包括传输操作),但 1 个传输队列),使用限制性更强的队列有什么优势吗? (2认同)

krO*_*oze 6

队列是一种接受命令缓冲区的事物,该命令缓冲区包含给定类型的操作(由家族标志提供)。提交到队列的命令具有提交顺序,因此它们受流水线障碍,子传递依赖项和事件的同步(在跨队列时,必须使用信号量或更好的选择)。

有一个窍门:COMPUTE并且GRAPHICS总是可以隐式接受TRANSFER工作负载(即使QueueFamilyProperties未列出它也是如此。请参见“ VkQueueFlagBits规范”下面的“注释”)。

传输适用于“复制”和“对齐”命令。稀疏类似于分页;它允许将多个内存句柄绑定到单个图像,并且以后也可以重新绑定不同的内存。

在规范中,在给定的vkCmd*命令下,它总是说出“支持的队列类型”。

队列族是一组与自己有特殊关系的队列。有些东西仅限于单个队列系列,例如图像(必须在队列系列之间传输)或命令池(仅创建命令缓冲区供给定队列系列使用,而不能由其他队列族使用)。从理论上讲,在某些外来设备上,可能会有更多具有相同标志的队列系列。

这就是Vulkan规范所保证的一切。在KhronosGroup / Vulkan-Docs#569上看到与此相关的问题


提供了一些特定于供应商的材料,例如:

GPU具有异步图形引擎,计算引擎和Copy \ DMA引擎。图形和计算当然会与GPU的相同计算单元竞争。

他们通常只有一个图形前端。这是图形操作的瓶颈,因此,使用多个图形队列毫无意义。

计算有两种操作模式:同步计算(公开为GRAPHICS|COMPUTE系列)和异步计算(公开为- COMPUTE仅系列)。第一个是安全的选择。第二个可以使您获得大约10%的性能,但比较棘手,需要更多的精力。AMD的文章建议始终将第一个作为基准。

从理论上讲,计算队列可以与GPU上的计算单元一样多。但是AMD认为,两个以上的异步计算队列没有任何好处,因此公开了很多。NVIDIA似乎保留了全部数量。

Copy \ DMA引擎(公开TRANSFER为-only系列)主要用于CPU⇄GPU传输。他们通常无法实现内部GPU副本的完整吞吐量。因此,除非有一些驱动程序魔术,否则应将异步传输系列用于CPU⇄GPU传输(以获取Async属性,从而能够不受阻碍地对其进行图形处理)。对于内部GPU副本,大多数情况下最好使用该GRAPHICS|TRANSFER系列。