Vulkan 学习(3)---- Vulkan 物理设备和队列组

Vulkan 物理设备

Vulkan 物理设备 (PhysicalDevice) 一般是指支持 Vulkan 的物理硬件,通常是系统的一部分-- 显卡、加速器、数字信号处理器或者其他的组件。
系统里有固定数量的物理设备,每个物理设备都有自己的一组固定的功能

vulkan 编程中,我们通常需要枚举出所有的物理设备,并且找到我们需要的那个:
vulkan 编程模型

枚举出物理设备

使用 vkEnumeratePhysicalDevices 枚举出所有的物理设备:

VkResult vkEnumeratePhysicalDevices(
    VkInstance                                  instance,
    uint32_t*                                   pPhysicalDeviceCount,
    VkPhysicalDevice*                           pPhysicalDevices);
  • instance 是之前使用 vkCreateInstance 创建的 VkInstance handle
  • pPhysicalDeviceCount 是用于指定或获取的物理设备数量,通过这个值返回物理设备的数量
  • pPhysicalDevices 要么是 nullptr 要么是数量不小于 pPhysicalDeviceCountVkPhysicalDevice 数组,通过这个值返回物理设备的属性

需要注意的是,如果pPhysicalDeviceCount 中指定的数量小于系统中的物理设备数量,则 pPhysicalDevices 中写入的物理设备不是所有,vkEnumeratePhysicalDevices 只会写入传入的 pPhysicalDeviceCount 个物理设备属性到 pPhysicalDevices 并返回 VkResult::VK_INCOMPLET
如果所有物理设备成功写入,则会返回 VkResult::VK_SUCCESS

获取物理设备属性

使用 vkGetPhysicalDeviceProperties() 函数获取物理设备信息
获取到的 PhysicalDeviceProperties 的含义如下:

typedef struct VkPhysicalDeviceProperties {
    uint32_t                            apiVersion;
    uint32_t                            driverVersion;
    uint32_t                            vendorID;
    uint32_t                            deviceID;
    VkPhysicalDeviceType                deviceType;
    char                                deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE];
    uint8_t                             pipelineCacheUUID[VK_UUID_SIZE];
    VkPhysicalDeviceLimits              limits;
    VkPhysicalDeviceSparseProperties    sparseProperties;
} VkPhysicalDeviceProperties;
  • apiVersion 该设备驱动支持的 Vulkan 版本
  • driverVersion 该设备驱动版本
  • vendorID 设备供应商的 ID
  • deviceID 设备的 ID
  • deviceType 设备类型
  • deviceName 设备名称
  • pipelineCacheUUID 设备的通用唯一识别码( universally unique identifier )
  • limits 设备的限制信息
  • sparseProperties 稀疏数据属性

其中的 deviceType 设备类型是下面的几种之一:

typedef enum VkPhysicalDeviceType {
    VK_PHYSICAL_DEVICE_TYPE_OTHER = 0,
    // 集成显卡
    VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU = 1,
    // 独立显卡
    VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU = 2,
    // 虚拟环境中虚拟显卡
    VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU = 3,
    // 中央处理器
    VK_PHYSICAL_DEVICE_TYPE_CPU = 4,
    VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM = 0x7FFFFFFF
} VkPhysicalDeviceType;

一般首选使用 VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU 独立显卡,之后再考虑使用 VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU 集成显卡

参考代码:
下面参考代码枚举出系统所有物理设备并且打印出了所有的物理设备属性:

void vulkanBasicDemo::vulkanCreatePhysicalDevice() {
    uint32_t physicalDeviceCount = 0;
    vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr);
    std::cout << "physicalDeviceCount:" << physicalDeviceCount << std::endl;

    //枚举物理设备
    std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
    vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices.data());

    for (const auto& device : physicalDevices) {
        VkPhysicalDeviceProperties deviceProperties;
        vkGetPhysicalDeviceProperties(device, &deviceProperties);
        std::cout << "apiVersion:" << deviceProperties.apiVersion << std::endl;
        std::cout << "driverVersion:" << deviceProperties.driverVersion << std::endl;
        std::cout << "vendorID:" << deviceProperties.vendorID << std::endl;
        std::cout << "deviceID:" << deviceProperties.deviceID << std::endl;
        std::cout << "deviceType:" << deviceProperties.deviceType << std::endl;
        std::cout << "deviceName:" << deviceProperties.deviceName << std::endl;

        // 这里可以根据应用程序的需求选择合适的设备
        if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) {
            // 选择该设备
            physicalDevice = device;
            break;
        }
    }
}

结果如下:

vkInstance create success!.
vulkanBasicDemo construct
vulkanBasicDemo destruct
vkInstance create success!.
vulkanBasicDemo construct
physicalDeviceCount:1
apiVersion:4206856
driverVersion:1659737
vendorID:32902
deviceID:42912
deviceType:1
deviceName:Intel(R) Iris(R) Xe Graphics // 本机的显卡是 Intel 自带的集成显卡

Vulkan 队列组

Vulkan中,设备队列是一个重要概念,应用程序通过将指令记录到指令缓存,然后提交到队列,而物理设备会读取设备队列中的任务,并且通过异步方式进行处理

Vulkan_queue

每个物理设备都会包含一个或者多个队列组(Queue Family),每个队列组这种包含一个或者多个队列,这些队列用于处理不同的任务,比如图形渲染,计算任务和传输操作
Vulkan QueueFamilu
Vulkan 将设备队列按照队列组的方式进行组织,规则如下:

  1. 一个队列组可以支持一个或者多个功能
  2. 一个队列组中包含一个或者多个队列
  3. 同一个队列组中的所有队列支持相同的功能
  4. 队列族之间可以有相同的功能,但两两队列之间不能有两个功能集
获取 QueueFamily 和 QueueFamilyProperty
void vkGetPhysicalDeviceQueueFamilyProperties(
VkPhysicalDevice                            physicalDevice,
uint32_t*                                   pQueueFamilyPropertyCount,
VkQueueFamilyProperties*                    pQueueFamilyProperties
);

使用上面的 vkGetPhysicalDeviceQueueFamilyProperties 或者设备的 QueueFamily信息,需要调用两次

使用vkGetPhysicalDeviceQueueFamilyProperties获取设备的QueueFamily属性,
其中 VkQueueFamilyProperties 定义如下:

typedef struct VkQueueFamilyProperties {
    VkQueueFlags    queueFlags;
    uint32_t        queueCount;
    uint32_t        timestampValidBits;
    VkExtent3D      minImageTransferGranularity;
} VkQueueFamilyPropertie
  • queueFlags是一个空白位码,表示队列族支持的操作类型
  • queueCount表示该队列中可用的队列数量
  • timestampValidBits表示时钟查询返回的有效时钟。该值为0表示该队列不支持时钟时钟。如果支持,时钟时钟返回的值将包含该时钟时钟的有效数据
  • minImageTransferGranularity表示该队列族支持的最小图像传输粒度。该粒度用于定义在执行图像传输操作(如复制或分层布局转换)时的最小单位

其中VkQueueFlags可用的值定义在VkQueueFlagBits中,定义如下:

typedef enum VkQueueFlagBits {
    VK_QUEUE_GRAPHICS_BIT = 0x00000001,//图形
    VK_QUEUE_COMPUTE_BIT = 0x00000002, //计算
    VK_QUEUE_TRANSFER_BIT = 0x00000004, //传输
    VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008, //稀疏绑定
    // 由 VK_VERSION_1_1 提供
    VK_QUEUE_PROTECTED_BIT = 0x00000010,
} VkQueueFlagBits;

Vulkan 的设备队列是用于提交命令方便GPU执行的关键机制,理解队列组和设备队列的工作原理,有助于更好好的使用Vulkan功能特性

参考代码:
下面函数用于枚举出物理设备所有的QueueFamily,并且打印出QueueFamily属性:

void vulkanBasicDemo::VulkanEnumrateQueueFamily() {
    uint32_t queueFamilyCount;
    vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);

    std::cout << "PhysicalDeviceQueueFamilyCount:" << queueFamilyCount << std::endl;


    std::vector<VkQueueFamilyProperties> queueFamilyProperties(queueFamilyCount);
    vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilyProperties.data());

    std::cout << "dump queueFamilyProperties:" << std::endl;
    for (const auto& queueFamilyPropery : queueFamilyProperties) {
        std::cout << "===================================" << std::endl;
        std::cout << "queueFamilyProperty queueFlags:" << queueFamilyPropery.queueFlags << std::endl;
        std::cout << "queueFamilyProperty queueFlags to string:" << queueflagtoString(queueFamilyPropery.queueFlags) << std::endl;
        std::cout << "queueFamilyProperty queueCount:" << queueFamilyPropery.queueCount << std::endl;
    }

    for (uint32_t i = 0; i < queueFamilyCount; i++) {
        if (queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
            graphicsQueueFamilyIndex = i;
            break;
        }
    }
    std::cout << "graphicsQueueFamilyIndex:" << graphicsQueueFamilyIndex << std::endl;
}

结果如下:

dump queueFamilyProperties:
===================================
queueFamilyProperty queueFlags:15 //0000 1111
queueFamilyProperty queueFlags to string:graphics computes transfer sparse
queueFamilyProperty queueCount:1
===================================
queueFamilyProperty queueFlags:32 //0010 0000
queueFamilyProperty queueFlags to string:video_decode
queueFamilyProperty queueCount:2
graphicsQueueFamilyIndex:0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值