[Vulkan教程]绘制一个三角形/设置/逻辑设备和队列(Logical device and queues)
介绍
选择一个物理设备之后,我们需要整个逻辑设备作为接口。物理设备的创建过程和实例相似,需要描述一下我们要用的特性。我们还要根据之前查到的队列簇指定要用的队列。你可以从同一个物理设备创建多个逻辑设备,如果你有需要。
新建一个成员变量来存储逻辑设备句柄。
VkDevice device;1
然后,创建一个createLogicalDevice函数并在initVulkan函数中调用。
void initVulkan() {createInstance();setupDebugMessenger();pickPhysicalDevice();createLogicalDevice();}void createLogicalDevice() {}12345678910
指定要创建队列
创建逻辑设备同样需要填充一些结构体,第一个就是VkDeviceQueueCreateInfo。这个结构体说明了我们要从队列簇创建的队列的数量。我们现在只关注图形队列。
QueueFamilyIndices indices = findQueueFamilies(physicalDevice);VkDeviceQueueCreateInfo queueCreateInfo{};queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value();queueCreateInfo.queueCount = 1;123456
当前可用的驱动程序只允许你为每个队列簇创建少量队列,实际上我们也不需要多个。你可以在多个线程上创建命令缓冲区,然后通过一次主线程上的低开销调用一次性提交所有命令缓冲区。
Vulkan允许你使用0.0到1.0之间的浮点数为队列分配优先级,以影响命令缓冲区的执行调度。
float queuePriority = 1.0f;queueCreateInfo.pQueuePriorities = &queuePriority;12
指定使用的设备特性
下一个要指定的我们会用到的设备特性。这些特性是我们在前面的章节通过vkGetPhysicalDevicefeatures函数查询的,像几何着色器等。现在我们还不需要什么花里胡哨的东西,直接指定VK_FALSE就行了。当我们要通过Vulkan做一些有趣的事情时,我们还会回头讨论这个结构体。
VkDeviceCreateInfo createInfo{};createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;12
创建逻辑设备
有了前面两个结构体后,我们就可以填充主要的结构体VkDeviceCreateInfo了。
VkDeviceCreateInfo createInfo{};createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;12
首先,指定队列创建信息和设备特性信息的指针:
createInfo.pQueueCreateInfos = &queueCreateInfo;createInfo.queueCreateInfoCount = 1;createInfo.pEnabledFeatures = &deviceFeatures;1234
剩下的信息和VkInstanceCreateInfo详细,要求你指定扩展和验证层信息。不同支持在于,这里指定的是特定与设备的。
举个例子就是,VK_KHR_swapchain扩展可以指定给特定设备,它允许我们将设备中渲染好的图片展现到窗口。系统中的Vulkan设备是有可能没有这种能力的,有可能它们只支持计算操作。我们还会在交换链一章中介绍这个扩展。
之前的Vulkan实现区分了实例和特定设备的验证层,但现在不再如此了。这意味着,在较新的Vulkan实现中,VkDeviceCreateInfo结构体中的enabledLayerCount字段和ppEnabledLayerNames字段会被忽略。为了和旧版兼容,我们最好还是设置一下它们:
createInfo.enabledExtensionCount = 0;if (enableValidationLayers) {createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());createInfo.ppEnabledLayerNames = validationLayers.data();} else {createInfo.enabledLayerCount = 0;}12345678
我们现在还不需要特定于设备的扩展。
好了,我们现在就可以用vkCreateDevice函数来实例化逻辑设备了。
if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {throw std::runtime_error("failed to create logical device!");}123
参数分别是:我们要用的物理设备、刚才创建的结构体指针、可选的分配器和存储逻辑设备句柄变量的指针。和实例创建函数一样,如果启用了不存在的扩展或指定了不支持的特性,它会返回错误。
设备需要在cleanup函数中通过vkDestroyDevice函数销毁:
void cleanup() {vkDestroyDevice(device, nullptr);...}1234
逻辑设备并不直接与实例交互,所以它不作为参数。
检索队列句柄
队列会与逻辑设备一起自动创建,但我们还没有句柄与它们交互。首先,添加一个类成员变量:
VkQueue graphicsQueue;1
设备队列会在设备销毁时隐式清理,所以我们不需要再cleanup时做什么。
我们可以使用vkGetDeviceQueue函数来根据队列簇检索队列句柄。参数分别是逻辑设备
队列簇、队列下标和一个指向保存队列句柄的指针。因为我们只创建了一个队列,我们简单地使用0。
vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);1
有了逻辑设备和队列句柄后,我们现在可以真正开始使用显卡做事情了!在接下来的几个章中,我们会设置资源以将结果呈现到窗口系统中。
