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
设备供应商的 IDdeviceID
设备的 IDdeviceType
设备类型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 //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