Vulkan 学习(3)---- Vulkan 物理设备和队列组
目录
- Vulkan 物理设备
- 枚举出物理设备
- 获取物理设备属性
- 参考代码
- Vulkan 队列组
- 获取设备 QueueFamily 和 QueueFamilyProperty
- 参考代码:
Vulkan 物理设备
Vulkan 物理设备 (PhysicalDevice
) 一般是指支持 Vulkan
的物理硬件,通常是系统的一部分-- 显卡、加速器、数字信号处理器或者其他的组件。
系统里有固定数量的物理设备,每个物理设备都有自己的一组固定的功能
在 vulkan
编程中,我们通常需要枚举出所有的物理设备,并且找到我们需要的那个:
枚举出物理设备
使用 vkEnumeratePhysicalDevices
枚举出所有的物理设备:
VkResult vkEnumeratePhysicalDevices(VkInstance instance,uint32_t* pPhysicalDeviceCount,VkPhysicalDevice* pPhysicalDevices);
instance
是之前使用vkCreateInstance
创建的VkInstance handle
pPhysicalDeviceCount
是用于指定或获取的物理设备数量,通过这个值返回物理设备的数量pPhysicalDevices
要么是 nullptr 要么是数量不小于pPhysicalDeviceCount
的VkPhysicalDevice
数组,通过这个值返回物理设备的属性
需要注意的是,如果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
中,设备队列是一个重要概念,应用程序通过将指令记录到指令缓存,然后提交到队列,而物理设备会读取设备队列中的任务,并且通过异步方式进行处理
每个物理设备都会包含一个或者多个队列组(Queue Family
),每个队列组这种包含一个或者多个队列,这些队列用于处理不同的任务,比如图形渲染,计算任务和传输操作
Vulkan 将设备队列按照队列组的方式进行组织,规则如下:
- 一个队列组可以支持一个或者多个功能
- 一个队列组中包含一个或者多个队列
- 同一个队列组中的所有队列支持相同的功能
- 队列族之间可以有相同的功能,但两两队列之间不能有两个功能集
获取设备 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
的设备队列是用于提交命令方便GP
U执行的关键机制,理解队列组和设备队列的工作原理,有助于更好好的使用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
queueFamilyProperty queueFlags to string:graphics computes transfer sparse
queueFamilyProperty queueCount:1
===================================
queueFamilyProperty queueFlags:32
queueFamilyProperty queueFlags to string:video_decode
queueFamilyProperty queueCount:2
graphicsQueueFamilyIndex:0