Vulkan中实际上是一个队列家庭?

时间:2019-03-21 01:47:04

标签: c++ gpu hardware vulkan

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

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

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;
}

这将产生以下输出:

Queue number: 16
Queue flags: {Graphics | Compute | Transfer | SparseBinding}
Queue number: 1
Queue flags: {Transfer}
Queue number: 8
Queue flags: {Compute}

因此,我天真地理解了这一点:

有3个队列家族,一个队列家族有16个队列,全部具有图形,计算,传输和稀疏绑定操作的能力(不知道最后两个是什么)

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

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

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

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

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

2 个答案:

答案 0 :(得分:6)

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

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

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

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

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

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

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

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

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

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

答案 1 :(得分:3)

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

有一个窍门:COMPUTEGRAPHICS总是可以隐式接受TRANSFER的工作负载(即使QueueFamilyProperties没有列出它)。

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

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

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

这几乎就是Vulkan规范所保证的一切。在KhronosGroup/Vulkan-Docs#569

看到与此有关的问题

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

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

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

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

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

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