如何实现Vulkan支持检查和动态加载器的C语言代码示例?
摘要:Vulkan Support Check and Dynamic Loader C++ code sample
很多时候不想静态依赖VulkanSDK所提供的静态库,因为会遇到一些过早的电脑不支持vulkan,
那么就需要使用动态加载vulkan-1.dll(for Windows)或libMoltenVK.dylib(for MacOS)的方式进行判断了。
VulkanSDK提供了相关头文件实现可以做到相关功能,仅需要include一下头文件 `vulkan/vulkan.hpp`,不需要再额外链接它的vulkan-1.lib文件
本段代码包含三个部分:
1. 判断是否支持vulkan(简单判断)
2. 从系统动态链接库(显卡厂商提供的dll)动态加载Vulkan,然后初始化。
3. 实际使用导出的函数,去遍历此电脑所有支持vulkan的设备。
那么直接贴代码:
1 #include "vulkan/vulkan.hpp"
2
3 struct GpuInfo
4 {
5 uint32_t api_version = 0;
6 uint32_t driver_version = 0;
7 uint32_t vendor_id = 0;
8 uint32_t device_id = 0;
9 char device_name[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE] = {0};
10 // 0 = discrete gpu
11 // 1 = integrated gpu
12 // 2 = virtual gpu
13 // 3 = cpu
14 int type = 0;
15 };
16
17 std::vector<GpuInfo> gpuInfos;
18
19 bool isSupportVulkan = false;
20 std::unique_ptr<vk::DynamicLoader> vulkanDynamicLoader;
21 std::unique_ptr<vk::DispatchLoaderDynamic> vulkanDispatchLoaderDynamic;
22
23 try
24 {
25 #ifdef WIN32
26 std::string vulkanLibraryFilePath = "vulkan-1.dll";
27 #else //APPLE
28 std::string vulkanLibraryFilePath = diropt::CurrentPath() + "/Contents/Frameworks/libMoltenVK.dylib";
29 #endif
30 vulkanDynamicLoader = std::make_unique<vk::DynamicLoader>(vulkanLibraryFilePath);
31 }
32 catch(std::runtime_error& ex)
33 {
34 isSupportVulkan = false;
35 return -1;
36 }
37 if(!vulkanDynamicLoader)
38 {
39 isSupportVulkan = false;
40 return -1;
41 }
42 isSupportVulkan = vulkanDynamicLoader->success();
43 if(!isSupportVulkan)
44 {
45 isSupportVulkan = false;
46 return -1;
47 }
48 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = vulkanDynamicLoader->getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
49 if(!vkGetInstanceProcAddr)
50 {
51 isSupportVulkan = false;
52 return -1;
53 }
54
55 // 用vkGetInstanceProcAddr指针去创建默认的分发器对象并初始化
56 vulkanDispatchLoaderDynamic = std::make_unique<vk::DispatchLoaderDynamic>(vkGetInstanceProcAddr);
57
58 VkResult ret;
59 VkInstance g_instance;
60 uint32_t instance_api_version = VK_MAKE_VERSION(1, 0, 0);
61 typedef VkResult(VKAPI_PTR * PFN_vkEnumerateInstanceVersion)(uint32_t * pApiVersion);
62 PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion =
63 (PFN_vkEnumerateInstanceVersion)vulkanDispatchLoaderDynamic->vkGetInstanceProcAddr(0, "vkEnumerateInstanceVersion");
64 if (vkEnumerateInstanceVersion)
65 {
66 //列举vulkan实例的版本
67 ret = vkEnumerateInstanceVersion(&instance_api_version);
68 if (ret != VK_SUCCESS)
69 {
70 return -1;
71 }
72 }
73
74 VkApplicationInfo applicationInfo;
75 applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
76 applicationInfo.pNext = 0;
77 applicationInfo.pApplicationName = "vulkan-check";
78 applicationInfo.applicationVersion = 0;
79 applicationInfo.pEngineName = "vulkan-check";
80 applicationInfo.engineVersion = 20230447;
81 applicationInfo.apiVersion = instance_api_version;
82
83 VkInstanceCreateInfo instanceCreateInfo;
84 instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
85 instanceCreateInfo.pNext = 0;
86 instanceCreateInfo.flags = 0;
87 instanceCreateInfo.pApplicationInfo = &applicationInfo;
88 instanceCreateInfo.enabledLayerCount = 0;
89 instanceCreateInfo.ppEnabledLayerNames = 0;
90 instanceCreateInfo.enabledExtensionCount = 0;
91 instanceCreateInfo.ppEnabledExtensionNames = 0;
92
93 VkInstance instance = 0;
94 //创建Vulkan实例
95 ret = vulkanDispatchLoaderDynamic->vkCreateInstance(&instanceCreateInfo, 0, &instance);
96 if (ret != VK_SUCCESS)
97 {
98 return -1;
99 }
100 g_instance = instance;
101
102 //再用已经创建好的Vulkan实例去进一步初始化vulkanDispatchLoaderDynamic
103 vulkanDispatchLoaderDynamic->init(instance, vkGetInstanceProcAddr);
104
105 //获取所有支持vulkan的设备列表
106 uint32_t physicalDeviceCount = 0;
107 ret = vulkanDispatchLoaderDynamic->vkEnumeratePhysicalDevices(g_instance, &physicalDeviceCount, 0);
108 if (ret != VK_SUCCESS)
109 {
110 return -1;
111 }
112 #define NCNN_MAX_GPU_COUNT 8
113 if (physicalDeviceCount > NCNN_MAX_GPU_COUNT)
114 physicalDeviceCount = NCNN_MAX_GPU_COUNT;
115
116 std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
117 //列举所有支持Vulkan的物理设备
118 ret = vulkanDispatchLoaderDynamic->vkEnumeratePhysicalDevices(g_instance, &physicalDeviceCount, physicalDevices.data());
119 if (ret != VK_SUCCESS)
120 {
121 return -1;
122 }
123
124 int gpu_info_index = 0;
125 for (uint32_t i = 0; i < physicalDeviceCount; i++)
126 {
127 const VkPhysicalDevice& physicalDevice = physicalDevices[i];
128
129 // device type
130 VkPhysicalDeviceProperties physicalDeviceProperties;
131 vulkanDispatchLoaderDynamic->vkGetPhysicalDeviceProperties(physicalDevice, &physicalDeviceProperties);
132
133 // info
134 GpuInfo gpu_info;
135 gpu_info.api_version = physicalDeviceProperties.apiVersion;
136 gpu_info.driver_version = physicalDeviceProperties.driverVersion;
137 gpu_info.vendor_id = physicalDeviceProperties.vendorID;
138 gpu_info.device_id = physicalDeviceProperties.deviceID;
139 memset(gpu_info.device_name, 0, VK_MAX_PHYSICAL_DEVICE_NAME_SIZE);
140 memcpy(gpu_info.device_name, physicalDeviceProperties.deviceName, strlen(physicalDeviceProperties.deviceName));
141
142 if (physicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER)
143 gpu_info.type = 0;
144 else if (physicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)
145 gpu_info.type = 1;
146 else if (physicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
147 gpu_info.type = 2;
148 else if (physicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU)
149 gpu_info.type = 3;
150 else if (physicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU)
151 gpu_info.type = 4;
152 else
153 gpu_info.type = -1;
154
155 gpu_info_index++;
156 gpuInfos.emplace_back(gpu_info);
157 }
