Vulkan学习--4.创建 VkDevice(逻辑设备) 和 VkQueue(队列)

• 为绘制和显示操作创建 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);

原文url:https://vulkan.lunarg.com/doc/sdk/1.1.101.0/windows/tutorial/html/02-enumerate_devices.html

猜你喜欢

转载自blog.csdn.net/yuxing55555/article/details/88869781