创建逻辑设备

填写好前面两个结构体后,我们可以开始填写VkDeviceCreateInfo结构体。

VkDeviceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;

将VkDeviceCreateInfo结构体的pQueueCreateInfos指针指向queueCreateInfo的地址,pEnabledFeatures指针指向deviceFeatures的地址:

createInfo.pQueueCreateInfos = &queueCreateInfo;
createInfo.queueCreateInfoCount = 1;

createInfo.pEnabledFeatures = &deviceFeatures;

结构体的其余成员和VkInstanceCreateInfo类似,不同的是这次的设置是针对设备的。

VK_KHR_swapchain就是一个设备特定扩展的例子,这一扩展使得我们可以将渲染的图像在窗口上显示出来。看起来似乎应该所有支持Vulkan的设备都应该支持这一扩展,然而,实际上有的Vulkan设备只支持计算指令,不支持这一图形相关扩展。在之后的章节,我们会对交换链进行更加深入地说明。

之前提到,我们可以对设备和Vulkan实例使用相同地校验层,不需要额外的扩展支持:

createInfo.enabledExtensionCount = 0;

if (enableValidationLayers) {
    createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
    createInfo.ppEnabledLayerNames = validationLayers.data();
} else {
    createInfo.enabledLayerCount = 0;
}

现在,我们可以调用vkCreateDevice函数创建逻辑设备了。

if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
    throw std::runtime_error("failed to create logical device!");
}

vkCreateDevice函数的参数包括我们创建的逻辑设备进行交互的物理设备对象,我们刚刚在结构体中指定的需要使用的队列信息,可选的分配器回调,以及用来存储返回的逻辑设备对象的内存地址。和Vulkan实例对象的创建函数类似,这一函数调用在请求的设备需求不被满足时返回错误代码。

逻辑设备对象创建后,应用程序结束前,需要我们自己在cleanup函数中调用vkDestroyDevice函数来清除它:

void cleanup() {
    vkDestroyDevice(device, nullptr);
        ...
}

逻辑设备并不直接与Vulkan实例交互,所以创建逻辑设备时不需要使用Vulkan实例作为参数。