创建一个逻辑设备
本节的代码是 03-init_device.cpp
下一步是创建一个VkDevice
逻辑设备对象,它对应于系统上的一个物理设备。逻辑设备是稍后用于将图形命令定向到硬件的关键对象。
到目前为止,我们的示例已经确定了你有多少物理设备。列举这些设备的示例工具函数确保了至少有一个设备,否则它会以不正确的断言停止。
选择一个设备
为了使事情变得简单,示例只从设备枚举返回的列表中取出第一个设备。你可以在“初始化设备”示例中看到它使用info.gpus[0]
。一个更复杂的程序可能需要额外的工作来决定如果有不止一个设备时要使用哪个。在这里,我们假设列表上的第一个设备足以满足本示例。
现在您已经选择了一个物理设备,您需要创建VkDevice
或逻辑设备对象。但是要做到这一点,您需要提供关于队列的一些信息。
设备队列和队列家族
与其他图形api不同,Vulkan将设备队列暴露给程序员,这样程序员就可以决定要使用多少队列以及要使用什么样的队列。
队列是用来向硬件提交命令的抽象机制。稍后您将看到一个Vulkan应用程序如何构建一个充满命令的命令缓冲区,然后将它们提交到一个队列中,以便由GPU硬件进行异步处理。
Vulkan将队列按照它们的类型排列成队列家族。为了找到您感兴趣的队列的类型和特征,您可以从物理设备查询QueueFamilyProperties:
typedef struct VkQueueFamilyProperties {
VkQueueFlags queueFlags;
uint32_t queueCount;
uint32_t timestampValidBits;
VkExtent3D minImageTransferGranularity;
} VkQueueFamilyProperties;
typedef enum VkQueueFlagBits {
VK_QUEUE_GRAPHICS_BIT = 0x00000001,
VK_QUEUE_COMPUTE_BIT = 0x00000002,
VK_QUEUE_TRANSFER_BIT = 0x00000004,
VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008,
} VkQueueFlagBits;
样例程序通过发出以下调用来获取队列信息:
vkGetPhysicalDeviceQueueFamilyProperties(
info.gpus[0], &info.queue_family_count, info.queue_props.data());
info.queue_props
是一个VkQueueFamilyProperties
类型的Vector。
您可以检查样例代码,以查看样例遵循枚举设备
中描述的模式,解释如何使用Vulkan API获取对象列表。它调用vkGetPhysicalDeviceQueueFamilyProperties
来获得计数,并再次调用它来获取数据。
VkQueueFamilyProperties
结构被称为“家族”,因为可能有许多(queueCount
)数量的队列,每个队列有一组特定的queueFlags
。例如,在一个拥有VK_QUEUE_GRAPHICS_BIT
集的家族中可能有8个队列。
在这样的设备上的队列和队列家族可能看起来像:
VkQueueFlagBits
指示每个硬件队列可以处理的工作负载类型。例如,一个设备可能会有个定义为VK_QUEUE_GRAPHICS_BIT
的队列家族,来处理3D图形工作。但是,如果同一设备有专门的硬件来做像素块拷贝(blits),它可能会定义另一个VK_QUEUE_TRANSFER_BIT
队列家族。这使得硬件可以让blit的工作负载和图形处理工作负载并行处理。
请注意,还有一个用于计算操作的队列类型,本教程中没有介绍这一类型。
一些简单的GPU硬件可能只有一个队列家族,但队列类的flag却被设置了多种:
现在,假设示例程序只针对绘制简单的图形,所以他们只需要查找图形位:
bool found = false;
for (unsigned int i = 0; i < info.queue_family_count; i++) {
if (info.queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
queue_info.queueFamilyIndex = i;
found = true;
break;
}
}
重要的是要注意,队列家族没有用句柄表示,而是用索引代替。
一旦您选择了一个队列家族索引,您就可以填充其余的结构:
float queue_priorities[1] = {0.0};
queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queue_info.pNext = NULL;
queue_info.queueCount = 1;
queue_info.pQueuePriorities = queue_priorities;
如果有超过一个队列,更复杂的程序可能希望在多个队列上提交图形命令。但是由于样本很简单,你只需要在这里要求一个。因为只有一个队列,所以在queue_priorities[]
中放置的值并不重要。
创建逻辑设备
随着队列问题的解决,创建逻辑设备非常简单:
VkDeviceCreateInfo device_info = {};
device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
device_info.pNext = NULL;
device_info.queueCreateInfoCount = 1;
device_info.pQueueCreateInfos = &queue_info;
device_info.enabledExtensionCount = 0;
device_info.ppEnabledExtensionNames = NULL;
device_info.enabledLayerCount = 0;
device_info.ppEnabledLayerNames = NULL;
device_info.pEnabledFeatures = NULL;
VkDevice device;
VkResult res = vkCreateDevice(info.gpus[0], &device_info, NULL, &device);
assert(res == VK_SUCCESS);
在本教程的这一点上,您不需要过多地担心扩展。您将很快获得使用扩展的机会。
至于层,设备层最近在Vulkan中被弃用,所以在创建设备时,不应该指定任何层。
现在您已经有了一个设备,您已经准备好了通过创建一个命令缓冲区来准备它接受图形命令。
© Copyright 2016 LunarG, Inc