物理设备

更新记录
  • 2024/1/2 增加该文档。

  • 2024/2/2 更新该文档。

  • 2024/2/2 增加 vkEnumeratePhysicalDevices 章节。

  • 2024/2/2 增加 获取物理设备信息 章节。

  • 2024/2/2 增加 vkGetPhysicalDeviceProperties 章节。

  • 2024/2/2 增加 VkPhysicalDeviceType 章节。

  • 2024/2/2 增加 VkPhysicalDeviceLimits 章节。

  • 2024/2/2 增加 VkPhysicalDeviceSparseProperties 章节。

  • 2024/2/2 增加 示例 章节。

一台主机上可能插着多个支持 Vulkan 的物理设备,为此 Vulkan 提供列举出系统中支持 Vulkan 的所有物理设备功能,开发者可通过 vkEnumeratePhysicalDevices(...) 函数进行物理设备列举。其定义如下:

vkEnumeratePhysicalDevices

// 由 VK_VERSION_1_0 提供
VkResult vkEnumeratePhysicalDevices(
    VkInstance                                  instance,
    uint32_t*                                   pPhysicalDeviceCount,
    VkPhysicalDevice*                           pPhysicalDevices);
  • instance 是之前使用 vkCreateInstance 创建的 VkInstance 句柄。

  • pPhysicalDeviceCount 是用于指定或获取的物理设备数量。

  • pPhysicalDevices 要么是 NULL 要么是数量不小于 pPhysicalDeviceCountVkPhysicalDevice 数组。

pPhysicalDevicesnullptr 时,该函数会将系统中支持 Vulkan 的设备数量写入 pPhysicalDeviceCount 中。

如果 pPhysicalDevices 为一个有效指针,则其指向一个 VkPhysicalDevice 数组,并且该数组长度 不能 小于 pPhysicalDeviceCount

如果 pPhysicalDeviceCount 中指定的数量小于系统中的物理设备数量,则 pPhysicalDevices 中写入的物理设备不是所有,则 vkEnumeratePhysicalDevices(...) 函数将会写入 pPhysicalDeviceCount 个物理设备到 pPhysicalDevices 数组中,并返回 VkResult::VK_INCOMPLETE

如果所有物理设备成功写入,则会返回 VkResult::VK_SUCCESS

因此,枚举所有物理设备需要调用 vkEnumeratePhysicalDevices(...) 两次:

  1. pPhysicalDevices 设置为 nullptr ,并通过 pPhysicalDeviceCount 获取支持系统中支持 Vulkan 的物理设备数量。

  2. 创建 pPhysicalDevices 数量的 VkPhysicalDevice 数组,并传入 pPhysicalDevices 中以获取系统中支持的 VkPhysicalDevice 物理设备。

VkInstance instance = 支持创建的 VkInstance;

uint32_t physical_device_count = 0;
vkEnumeratePhysicalDevices(instance, &physical_device_count, nullptr);

std::vector<VkPhysicalDevice> physical_devices(physical_device_count);
vkEnumeratePhysicalDevices(instance, &physical_device_count, physical_devices.data());

这样就可以枚举出系统中支持 Vulkan 的所有物理设备。

备注

枚举的 VkPhysicalDevice 句柄是在调用 vkCreateInstance(...) 创建 VkInstance 时驱动内部创建的。换句话说就是: VkPhysicalDevice 句柄的生命周期与 VkInstance 相同, VkInstance 创建 VkPhysicalDevice 句柄们也会创建, VkInstance 销毁 VkPhysicalDevice 句柄们也会销毁。

获取物理设备信息

在通过 vkEnumeratePhysicalDevices(...) 获取系统中支持的物理设备句柄后,我们需要筛选出我们需要的物理设备,比如,相比于使用集成显卡,我们往往倾向于使用性能更强悍的独立显卡。

我们可以通过 vkGetPhysicalDeviceProperties(...) 函数获取物理设备信息:

vkGetPhysicalDeviceProperties

// 由 VK_VERSION_1_0 提供
void vkGetPhysicalDeviceProperties(
    VkPhysicalDevice                            physicalDevice,
    VkPhysicalDeviceProperties*                 pProperties);
  • physicalDevice 对应要获取属性的物理设备的句柄。

  • pProperties 对应返回的物理设备属性。

其中 VkPhysicalDeviceProperties 定义如下:

VkPhysicalDeviceProperties

// 由 VK_VERSION_1_0 提供
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 稀疏数据属性。

其中 apiVersion 是最为 重要 的参数,该参数表明该设备支持的 Vulkan 最高版本。该版本与 VkApplicationInfo::apiVersion 中的版本类似,您只能获取到 VkPhysicalDeviceProperties::apiVersion 版本及之前版本 Vulkan 对应的 Device 域函数

所以能够获取到的 Vulkan 函数逻辑如下:

        flowchart TD
   VulkanAPI["Vulkan 某一个函数"]
   IsLessEqualInstanceVulkanVersion{"小于等于 VkApplicationInfo::apiVersion 版本"}
   IsInstanceAPI{"为 Instance/PhysicalDevice 域函数"}
   IsDeviceAPI{"为 Device 域函数"}
   IsLessEqualDeviceVulkanVersion{"小于等于 VkPhysicalDeviceProperties::apiVersion 版本"}

   ReturnValidAPI["返回有效API"]
   ReturnNullAPI["返回无效API\n(nullptr)"]

   VulkanAPI-->IsLessEqualInstanceVulkanVersion
   IsLessEqualInstanceVulkanVersion--是-->IsInstanceAPI
   IsLessEqualInstanceVulkanVersion--否-->ReturnNullAPI

   IsInstanceAPI--是-->ReturnValidAPI
   IsInstanceAPI--否-->IsDeviceAPI

   IsDeviceAPI--是-->IsLessEqualDeviceVulkanVersion
   IsDeviceAPI--否-->Undefine["未定义\n一个函数不可能 既不是 Instance/PhysicalDevice 域函数也不是 Device 域函数"]

   IsLessEqualDeviceVulkanVersion--是-->ReturnValidAPI
   IsLessEqualDeviceVulkanVersion--否-->ReturnNullAPI
    

其中 VkPhysicalDeviceType 定义如下:

VkPhysicalDeviceType

// 由 VK_VERSION_1_0 提供
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,
} VkPhysicalDeviceType;
  • VK_PHYSICAL_DEVICE_TYPE_OTHER 该设备类型不与任何其他类型匹配, Vulkan 中未定义的设备类型。

  • VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU 集成显卡。

  • VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU 独立显卡。

  • VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU 虚拟环境中的虚拟显卡。

  • VK_PHYSICAL_DEVICE_TYPE_CPU 中央处理器( CPU )。

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

其中 VkPhysicalDeviceLimits 用于表述该设备的一些限制,比如最大支持的图片像素大小。

VkPhysicalDeviceLimits

备注

由于该结构体中有很多还没有涉及到的知识,并且成员过多,所以这里只给出定义。用户只需要知道该结构体中存有物理设备的上限信息即可。

// 由 VK_VERSION_1_0 提供
typedef struct VkPhysicalDeviceLimits {
    uint32_t              maxImageDimension1D;
    uint32_t              maxImageDimension2D;
    uint32_t              maxImageDimension3D;
    uint32_t              maxImageDimensionCube;
    uint32_t              maxImageArrayLayers;
    uint32_t              maxTexelBufferElements;
    uint32_t              maxUniformBufferRange;
    uint32_t              maxStorageBufferRange;
    uint32_t              maxPushConstantsSize;
    uint32_t              maxMemoryAllocationCount;
    uint32_t              maxSamplerAllocationCount;
    VkDeviceSize          bufferImageGranularity;
    VkDeviceSize          sparseAddressSpaceSize;
    uint32_t              maxBoundDescriptorSets;
    uint32_t              maxPerStageDescriptorSamplers;
    uint32_t              maxPerStageDescriptorUniformBuffers;
    uint32_t              maxPerStageDescriptorStorageBuffers;
    uint32_t              maxPerStageDescriptorSampledImages;
    uint32_t              maxPerStageDescriptorStorageImages;
    uint32_t              maxPerStageDescriptorInputAttachments;
    uint32_t              maxPerStageResources;
    uint32_t              maxDescriptorSetSamplers;
    uint32_t              maxDescriptorSetUniformBuffers;
    uint32_t              maxDescriptorSetUniformBuffersDynamic;
    uint32_t              maxDescriptorSetStorageBuffers;
    uint32_t              maxDescriptorSetStorageBuffersDynamic;
    uint32_t              maxDescriptorSetSampledImages;
    uint32_t              maxDescriptorSetStorageImages;
    uint32_t              maxDescriptorSetInputAttachments;
    uint32_t              maxVertexInputAttributes;
    uint32_t              maxVertexInputBindings;
    uint32_t              maxVertexInputAttributeOffset;
    uint32_t              maxVertexInputBindingStride;
    uint32_t              maxVertexOutputComponents;
    uint32_t              maxTessellationGenerationLevel;
    uint32_t              maxTessellationPatchSize;
    uint32_t              maxTessellationControlPerVertexInputComponents;
    uint32_t              maxTessellationControlPerVertexOutputComponents;
    uint32_t              maxTessellationControlPerPatchOutputComponents;
    uint32_t              maxTessellationControlTotalOutputComponents;
    uint32_t              maxTessellationEvaluationInputComponents;
    uint32_t              maxTessellationEvaluationOutputComponents;
    uint32_t              maxGeometryShaderInvocations;
    uint32_t              maxGeometryInputComponents;
    uint32_t              maxGeometryOutputComponents;
    uint32_t              maxGeometryOutputVertices;
    uint32_t              maxGeometryTotalOutputComponents;
    uint32_t              maxFragmentInputComponents;
    uint32_t              maxFragmentOutputAttachments;
    uint32_t              maxFragmentDualSrcAttachments;
    uint32_t              maxFragmentCombinedOutputResources;
    uint32_t              maxComputeSharedMemorySize;
    uint32_t              maxComputeWorkGroupCount[3];
    uint32_t              maxComputeWorkGroupInvocations;
    uint32_t              maxComputeWorkGroupSize[3];
    uint32_t              subPixelPrecisionBits;
    uint32_t              subTexelPrecisionBits;
    uint32_t              mipmapPrecisionBits;
    uint32_t              maxDrawIndexedIndexValue;
    uint32_t              maxDrawIndirectCount;
    float                 maxSamplerLodBias;
    float                 maxSamplerAnisotropy;
    uint32_t              maxViewports;
    uint32_t              maxViewportDimensions[2];
    float                 viewportBoundsRange[2];
    uint32_t              viewportSubPixelBits;
    size_t                minMemoryMapAlignment;
    VkDeviceSize          minTexelBufferOffsetAlignment;
    VkDeviceSize          minUniformBufferOffsetAlignment;
    VkDeviceSize          minStorageBufferOffsetAlignment;
    int32_t               minTexelOffset;
    uint32_t              maxTexelOffset;
    int32_t               minTexelGatherOffset;
    uint32_t              maxTexelGatherOffset;
    float                 minInterpolationOffset;
    float                 maxInterpolationOffset;
    uint32_t              subPixelInterpolationOffsetBits;
    uint32_t              maxFramebufferWidth;
    uint32_t              maxFramebufferHeight;
    uint32_t              maxFramebufferLayers;
    VkSampleCountFlags    framebufferColorSampleCounts;
    VkSampleCountFlags    framebufferDepthSampleCounts;
    VkSampleCountFlags    framebufferStencilSampleCounts;
    VkSampleCountFlags    framebufferNoAttachmentsSampleCounts;
    uint32_t              maxColorAttachments;
    VkSampleCountFlags    sampledImageColorSampleCounts;
    VkSampleCountFlags    sampledImageIntegerSampleCounts;
    VkSampleCountFlags    sampledImageDepthSampleCounts;
    VkSampleCountFlags    sampledImageStencilSampleCounts;
    VkSampleCountFlags    storageImageSampleCounts;
    uint32_t              maxSampleMaskWords;
    VkBool32              timestampComputeAndGraphics;
    float                 timestampPeriod;
    uint32_t              maxClipDistances;
    uint32_t              maxCullDistances;
    uint32_t              maxCombinedClipAndCullDistances;
    uint32_t              discreteQueuePriorities;
    float                 pointSizeRange[2];
    float                 lineWidthRange[2];
    float                 pointSizeGranularity;
    float                 lineWidthGranularity;
    VkBool32              strictLines;
    VkBool32              standardSampleLocations;
    VkDeviceSize          optimalBufferCopyOffsetAlignment;
    VkDeviceSize          optimalBufferCopyRowPitchAlignment;
    VkDeviceSize          nonCoherentAtomSize;
} VkPhysicalDeviceLimits;

VkPhysicalDeviceSparseProperties

VkPhysicalDeviceSparseProperties

将会在 稀疏 资源章节中进行讲解。

示例

VkInstance instance = 支持创建的 VkInstance;

uint32_t physical_device_count = 0;
vkEnumeratePhysicalDevices(instance, &physical_device_count, nullptr);

std::vector<VkPhysicalDevice> physical_devices(physical_device_count);
vkEnumeratePhysicalDevices(instance, &physical_device_count, physical_devices.data());

VkPhysicalDevice target_physical_device = VK_NULL_HANDLE;

for(VkPhysicalDevice& physical_device : physical_devices)
{
   VkPhysicalDeviceProperties physical_device_properties = {};
   vkGetPhysicalDeviceProperties(physical_device, &physical_device_properties);

   if(physical_device_properties.deviceType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
   {
      target_physical_device = physical_device;
      break;
   }
   else if(physical_device_properties.deviceType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)
   {
      target_physical_device = physical_device;
   }
}

if(target_physical_device == VK_NULL_HANDLE)
{
   throw std::runtime_error("没有找到合适的物理设备");
}