• 为绘制和显示操作创建 VkDevice(逻辑设备) 和 VkQueue(队列)
选择物理设备后,我们还需要一个逻辑设备来作为和物理设备交互的接口。
我们还需要指定使用的队列所属的队列族。
对于同一个物理设备,我们可以根据需求的不同,创建多个逻辑设备。
部分示例:
VkDevice device;//逻辑设备--3
VkQueue graphicsQueue;//逻辑设备的队列句柄,自动被清除--3
//创建一个逻辑设备--3
void createLogicalDevice(){
//获取带有图形能力的队列族
QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
/**
目前而言,对于每个队列族,驱动程序只允许创建很少数量的队列,但实际上,
对于每一个队列族,我们很少需要一个以上的队列。
我们可以在多个线程创建指令缓冲,然后在主线程一次将它们全部提交,降低调用开销。
Vulkan需要我们赋予队列一个0.0到1.0之间的浮点数作为优先级来控制指令缓冲的执行顺序。
即使只有一个队列,我们也要显式地赋予队列优先级
*/
float queuePriority = 1.0f;
//描述了针对一个队列族我们所需的队列数量。
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex=indices.graphicsFamily;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &queuePriority;
//指定应用程序使用的设备特性
VkPhysicalDeviceFeatures deviceFeatures = {};
//创建逻辑设备相关信息
VkDeviceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.queueCreateInfoCount = 1;
createInfo.pQueueCreateInfos = &queueCreateInfo;
createInfo.pEnabledFeatures = &deviceFeatures;
if(enableValidationLayers){
//以对设备和 Vulkan 实例使用相同地校验层
createInfo.enabledLayerCount =
static_cast<uint32_t>(validataionLayers.size());
createInfo.ppEnabledLayerNames = validataionLayers.data();
}else{
createInfo.enabledLayerCount = 0;
}
/**
vkCreateDevice 函数的参数:
1.创建的逻辑设备进行交互的物理设备对象
2.指定的需要使用的队列信息
3.可选的分配器回调
4.用来存储返回的逻辑设备对象的内存地址
逻辑设备并不直接与Vulkan实例交互,所以创建逻辑设备时不需要使用Vulkan实例作为参数
*/
//创建逻辑设备
if(vkCreateDevice(physicalDevice,&createInfo,nullptr,
&device) != VK_SUCCESS){
throw std::runtime_error("failed to create logical device!");
}
//获取指定队列族的队列句柄
//它的参数依次是逻辑设备对象,队列族索引,队列索引,用来存储返回的队列句柄的内存地址
//我们只创建了一个队列,所以,可以直接使用索引 0
vkGetDeviceQueue(device,indices.graphicsFamily,0,&graphicsQueue);
}
设备队列和队列系列
与其他图形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;
该VkQueueFamilyProperties结构称为“系列”,因为可能有许多(queueCount)队列可用于特定的一组queueFlags。例如,一个系列中可能有8个具有该VK_QUEUE_GRAPHICS_BIT组的队列。
设备上的队列和队列系列可能如下所示:
该VkQueueFlagBits指示每个硬件队列可以处理什么类型的工作负载。例如,设备可以定义VK_QUEUE_GRAPHICS_BIT为正常3D图形工作设置的队列族 。但是,如果同一设备具有用于执行像素块复制(blits)的专用硬件,则它可以仅使用该VK_QUEUE_TRANSFER_BIT 集定义另一个队列系列。这使得硬件可以与blit工作负载并行处理图形工作负载。
一些更简单的GPU硬件可能只通告一个具有多个队列类型标志集的队列系列:
创建设备
///4.创建设备
VkDeviceQueueCreateInfo queue_info = {};
uint32_t queue_family_count=0;
vkGetPhysicalDeviceQueueFamilyProperties(gpus[0], &queue_family_count, NULL);
assert(queue_family_count >= 1);
std::vector<VkQueueFamilyProperties> queue_props(queue_family_count);
//获取队列信息
vkGetPhysicalDeviceQueueFamilyProperties(gpus[0], &queue_family_count,
queue_props.data());
std::cout <<queue_family_count<<" "<<queue_props.size() <<std::endl;
bool found = false;
for (unsigned int i = 0; i < queue_family_count; i++) {
//现在只对绘制简单图形感兴趣,所以只需要查找图形位
if (queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
//note:队列系列不是用句柄表示,而是用索引表示
queue_info.queueFamilyIndex = i;
found = true;
break;
}
}
std::cout <<"found:"<< found << std::endl;
//如果有多个队列可用,则更复杂的程序可能希望在多个队列上提交图形命令
//这里只有一个队列,所以放入的值queue_priorities[] 并不重要
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;
//构建设备信息
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;
//最近在Vulkan中不推荐使用设备图层,因此在创建设备时不必指定任何图层
device_info.enabledLayerCount = 0;
device_info.ppEnabledLayerNames = NULL;
device_info.pEnabledFeatures = NULL;
VkDevice device;//创建设备
res = vkCreateDevice(gpus[0], &device_info, NULL, &device);
assert(res == VK_SUCCESS);
///
vkDestroyDevice(device, NULL);