4.3. 队列
4.3.1. 队列族的属性
如之前的Physical Device Enumeration一节讲过,vkGetPhysicalDeviceQueueFamilyProperties
命令是用来获取设备所支持的队列族的属性的。
vkGetPhysicalDeviceQueueFamilyProperties
返回的pQueueFamilyProperties
数组的每一个索引描述了 物理设备上唯一的队列族。 这些索引被用来创建队列,它们通过下节Queue Creation 讲解的VkDeviceQueueCreateInfo
类型数据 直接对应着传递给 vkCreateDevice
命令的 queueFamilyIndex
。
同一个物理设备上的队列族的分组取决于Vulkan实现。
注意
可以假定一个物理设备会把能力匹配的所有队列分组到一个族。 然而,这是对Vulkan实现的一个推荐,很可能,物理设备可能会返回两个拥有相同能力的族。 |
一旦应用程序以它想用的族来确定了物理设备,它将结合逻辑设备来创建这些队列。 这在下节讲述。
4.3.2. 创建队列
创建一个逻辑设备也会创建该设备相关的队列。需要创建的队列通过传递给vkCreateDevice
的参数pQueueCreateInfos
的一系列VkDeviceQueueCreateInfo
类型数据描述。
队列通过VkQueue
handles表示:
VK_DEFINE_HANDLE(VkQueue)
VkDeviceQueueCreateInfo
数据结构定义如下:
typedef struct VkDeviceQueueCreateInfo {
VkStructureType sType;
const void* pNext;
VkDeviceQueueCreateFlags flags;
uint32_t queueFamilyIndex;
uint32_t queueCount;
const float* pQueuePriorities;
} VkDeviceQueueCreateInfo;
-
sType
是数据结构的类型。 -
pNext
是NULL
或者一个指向拓展特定的数据结构。 -
flags
被保留使用。 -
queueFamilyIndex
是一个无符号整形数字,表示在此设备上创建的队列族的索引。 这个索引对应着vkGetPhysicalDeviceQueueFamilyProperties
返回的pQueueFamilyProperties
数组中的元素的索引。 -
queueCount
是一个无符号整形数字,指定了queueFamilyIndex
表示的队列族中的队列个数。 -
pQueuePriorities
是一个元素为归一化浮点数的数组,大小为queueCount
,指定了提交到已创建队列的任务的优先级。 参考Queue Priority 以获取更多信息。
可调用如下命令来获取一个队列的handle:
void vkGetDeviceQueue(
VkDevice device,
uint32_t queueFamilyIndex,
uint32_t queueIndex,
VkQueue* pQueue);
-
device
是拥有该队列的逻辑设备。 -
queueFamilyIndex
是队列所属的队列族的索引。 -
queueIndex
是需要获取的队列在队列族中的索引。 -
pQueue
是一个指针,指向了VkQueue
对象,它将被获取到的队列的handle所覆盖。
4.3.3. 队列族索引
队列族索引在Vulkan中多处被使用,它可以把操作和特定的队列族绑定起来。
当通过vkGetDeviceQueue
获取到一个队列的handle时,队列族的索引被用来选择从哪个队列族中获取队列handle(上上节所述)。
当创建VkCommandPool
对象时(参考 Command Pools),一个队列族索引通过 VkCommandPoolCreateInfo
数据结构指定。 从缓存池中获取的命令缓冲区 只能提交到对应着此队列族的队列上。
当创建 VkImage
(参考 Images) 和 VkBuffer
(参考 Buffers) 资源时,一系列的队列族被包含到VkImageCreateInfo
和VkBufferCreateInfo
数据结构中,来指定可以访问到这些资源的队列族。
当插入一个VkBufferMemoryBarrier
或 VkImageMemoryBarrier
(see 事件) 时,一个源和目标队列族索引被指定来允许把缓冲区或图像转移到另外一个队列族。 参考 Resource Sharing 一节以获取细节。
4.3.4. 队列优先级
每一个队列都被分配了一个优先级,在创建设备过程中VkDeviceQueueCreateInfo
的数据结构中设置的。 每一个队列的优先级是一个归一化的浮点值,在0.0和1.0之间,然后有Vulkan实现转换到离散的的优先级级别。 高的值表示更高的优先级,0.0表示最低的优先级,1.0表示最高。一个设备内,拥有高优先级的队列将会比 优先级低的队列获得更多的处理时间。Vulkan实现不保证相同优先级的队列的排序或者调度, 不管explicit synchronization primitives中已定义的限制如何。 Vulkan实现不保证不同设备之间的队列要如何安排优先级。
一个Vulkan实现也许允许更高优先级的队列会让同一个设备上的低优先级的队列挨饿,直到自己完成所有命令的执行。 队列优先级的关系必须不能导致另外一个设备上的队列暂停工作。
没有任何明确的保证高优先级的队列比低优先级的队列接受更多的处理时间和服务质量。
4.3.5. 队列提交
通过_队列提交_命令,如vkQueueSubmit
,工作就被提交到队列了。队列提交命令定义了一系列的需要 物理设备执行的_队列操作_,包括使用信号量和栅栏来同步。
提交命令接受目标设备为参数,零个或者多个_batches_的任务,和一个可选的栅栏来激发任务完成的信号。 每一个批次由三个部分组成:
-
Zero or more semaphores to wait on before execution of the rest of the batch.
-
If present, these describe a semaphore wait operation.
-
-
Zero or more work items to execute.
-
If present, these describe a queue operation matching the work described.
-
-
Zero or more semaphores to signal upon completion of the work items.
-
If present, these describe a semaphore signal operation.
-
If a fence is present in a queue submission, it describes a fence signal operation.
通过一个队列提交命令描述的所有任务必须在命令返回前提交到队列。
稀疏内存绑定
在Vulkan里,可以稀疏的绑定内存到缓冲区或者图像,这点在前面的Sparse Resource一章讲过。 稀疏内存绑定是一个队列操作。包含VK_QUEUE_SPARSE_BINDING_BIT
标志的队列必须能支持在设备上 映射虚拟地址到物理地址。这将导致设备上映射表的更新。这个更新必须让队列保持同步,以避免在图形命令执行期间 损坏page table映射。通过在队列上绑定稀疏内存,所有依赖于被更新的绑定的命令在绑定更新之后同步的执行。 查看 Synchronization and Cache Control 一章可知如何实现同步。
4.3.6. 队列销毁
队列和vkCreateDevice
创建的逻辑设备一同被创建。当调用vkDestroyDevice
后,和逻辑设备关联的所有队列都被销毁了。