1 //
2 // Copyright 2020 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // SystemInfo_vulkan.cpp: Generic vulkan implementation of SystemInfo.h
8 // TODO: Use VK_KHR_driver_properties. http://anglebug.com/42263671
9
10 #include "gpu_info_util/SystemInfo_vulkan.h"
11
12 #include <vulkan/vulkan.h>
13 #include "gpu_info_util/SystemInfo_internal.h"
14
15 #include <algorithm>
16 #include <cstring>
17 #include <fstream>
18 #include <string>
19 #include <vector>
20
21 #include "common/angleutils.h"
22 #include "common/debug.h"
23 #include "common/platform_helpers.h"
24 #include "common/system_utils.h"
25 #include "common/vulkan/libvulkan_loader.h"
26
27 namespace angle
28 {
29 namespace
30 {
31 // Note: most drivers use VK_MAKE_API_VERSION to create the version.
ParseGenericDriverVersion(uint32_t driverVersion)32 VersionInfo ParseGenericDriverVersion(uint32_t driverVersion)
33 {
34 VersionInfo version = {};
35
36 version.major = VK_API_VERSION_MAJOR(driverVersion);
37 version.minor = VK_API_VERSION_MINOR(driverVersion);
38 version.subMinor = VK_API_VERSION_PATCH(driverVersion);
39
40 return version;
41 }
42 } // namespace
43
ParseAMDVulkanDriverVersion(uint32_t driverVersion)44 VersionInfo ParseAMDVulkanDriverVersion(uint32_t driverVersion)
45 {
46 return ParseGenericDriverVersion(driverVersion);
47 }
48
ParseArmVulkanDriverVersion(uint32_t driverVersion)49 VersionInfo ParseArmVulkanDriverVersion(uint32_t driverVersion)
50 {
51 return ParseGenericDriverVersion(driverVersion);
52 }
53
ParseBroadcomVulkanDriverVersion(uint32_t driverVersion)54 VersionInfo ParseBroadcomVulkanDriverVersion(uint32_t driverVersion)
55 {
56 return ParseGenericDriverVersion(driverVersion);
57 }
58
ParseSwiftShaderVulkanDriverVersion(uint32_t driverVersion)59 VersionInfo ParseSwiftShaderVulkanDriverVersion(uint32_t driverVersion)
60 {
61 return ParseGenericDriverVersion(driverVersion);
62 }
63
ParseImaginationVulkanDriverVersion(uint32_t driverVersion)64 VersionInfo ParseImaginationVulkanDriverVersion(uint32_t driverVersion)
65 {
66 return ParseGenericDriverVersion(driverVersion);
67 }
68
ParseIntelWindowsVulkanDriverVersion(uint32_t driverVersion)69 VersionInfo ParseIntelWindowsVulkanDriverVersion(uint32_t driverVersion)
70 {
71 VersionInfo version = {};
72
73 // Windows Intel driver versions are built in the following format:
74 //
75 // Major (18 bits) | Minor (14 bits)
76 //
77 version.major = driverVersion >> 14;
78 version.minor = driverVersion & 0x3FFF;
79
80 return version;
81 }
82
ParseKazanVulkanDriverVersion(uint32_t driverVersion)83 VersionInfo ParseKazanVulkanDriverVersion(uint32_t driverVersion)
84 {
85 return ParseGenericDriverVersion(driverVersion);
86 }
87
ParseNvidiaVulkanDriverVersion(uint32_t driverVersion)88 VersionInfo ParseNvidiaVulkanDriverVersion(uint32_t driverVersion)
89 {
90 VersionInfo version = {};
91
92 version.major = driverVersion >> 22;
93 version.minor = driverVersion >> 14 & 0xFF;
94 version.subMinor = driverVersion >> 6 & 0xFF;
95 version.patch = driverVersion & 0x3F;
96
97 return version;
98 }
99
ParseQualcommVulkanDriverVersion(uint32_t driverVersion)100 VersionInfo ParseQualcommVulkanDriverVersion(uint32_t driverVersion)
101 {
102 if ((driverVersion & 0x80000000) != 0)
103 {
104 return ParseGenericDriverVersion(driverVersion);
105 }
106
107 // Older drivers with an unknown format, consider them version 0.
108 VersionInfo version = {};
109 version.minor = driverVersion;
110 return version;
111 }
112
ParseSamsungVulkanDriverVersion(uint32_t driverVersion)113 VersionInfo ParseSamsungVulkanDriverVersion(uint32_t driverVersion)
114 {
115 return ParseGenericDriverVersion(driverVersion);
116 }
117
ParseVeriSiliconVulkanDriverVersion(uint32_t driverVersion)118 VersionInfo ParseVeriSiliconVulkanDriverVersion(uint32_t driverVersion)
119 {
120 return ParseGenericDriverVersion(driverVersion);
121 }
122
ParseVivanteVulkanDriverVersion(uint32_t driverVersion)123 VersionInfo ParseVivanteVulkanDriverVersion(uint32_t driverVersion)
124 {
125 return ParseGenericDriverVersion(driverVersion);
126 }
127
ParseMesaVulkanDriverVersion(uint32_t driverVersion)128 VersionInfo ParseMesaVulkanDriverVersion(uint32_t driverVersion)
129 {
130 return ParseGenericDriverVersion(driverVersion);
131 }
132
ParseMoltenVulkanDriverVersion(uint32_t driverVersion)133 VersionInfo ParseMoltenVulkanDriverVersion(uint32_t driverVersion)
134 {
135 // Note: MoltenVK formulates its version number as a decimal number like so:
136 // (major * 10000) + (minor * 100) + patch
137 VersionInfo version = {};
138
139 version.major = driverVersion / 10000;
140 version.minor = (driverVersion / 100) % 100;
141 version.patch = driverVersion % 100;
142
143 return version;
144 }
145
146 class VulkanLibrary final : NonCopyable
147 {
148 public:
149 VulkanLibrary() = default;
150
~VulkanLibrary()151 ~VulkanLibrary()
152 {
153 if (mInstance != VK_NULL_HANDLE)
154 {
155 auto pfnDestroyInstance = getProc<PFN_vkDestroyInstance>("vkDestroyInstance");
156 if (pfnDestroyInstance)
157 {
158 pfnDestroyInstance(mInstance, nullptr);
159 }
160 }
161
162 CloseSystemLibrary(mLibVulkan);
163 }
164
GetInstanceExtensionNames() const165 std::vector<std::string> GetInstanceExtensionNames() const
166 {
167 std::vector<std::string> extensionNames;
168
169 auto pfnEnumerateInstanceExtensionProperties =
170 getProc<PFN_vkEnumerateInstanceExtensionProperties>(
171 "vkEnumerateInstanceExtensionProperties");
172 if (!pfnEnumerateInstanceExtensionProperties)
173 {
174 return extensionNames;
175 }
176
177 uint32_t extensionCount = 0;
178 if (pfnEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr) !=
179 VK_SUCCESS)
180 {
181 return extensionNames;
182 }
183
184 std::vector<VkExtensionProperties> extensions(extensionCount);
185 if (pfnEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()) !=
186 VK_SUCCESS)
187 {
188 return extensionNames;
189 }
190
191 for (const auto &extension : extensions)
192 {
193 extensionNames.emplace_back(extension.extensionName);
194 }
195
196 std::sort(extensionNames.begin(), extensionNames.end());
197
198 return extensionNames;
199 }
200
ExtensionFound(std::string const & needle,const std::vector<std::string> & haystack)201 bool ExtensionFound(std::string const &needle, const std::vector<std::string> &haystack)
202 {
203 // NOTE: The list must be sorted.
204 return std::binary_search(haystack.begin(), haystack.end(), needle);
205 }
206
getVulkanInstance()207 VkInstance getVulkanInstance()
208 {
209 mLibVulkan = vk::OpenLibVulkan();
210 if (!mLibVulkan)
211 {
212 // If Vulkan doesn't exist, bail-out early:
213 return VK_NULL_HANDLE;
214 }
215
216 // Determine the available Vulkan instance version:
217 uint32_t instanceVersion = VK_API_VERSION_1_0;
218 #if defined(VK_VERSION_1_1)
219 auto pfnEnumerateInstanceVersion =
220 getProc<PFN_vkEnumerateInstanceVersion>("vkEnumerateInstanceVersion");
221 if (!pfnEnumerateInstanceVersion ||
222 pfnEnumerateInstanceVersion(&instanceVersion) != VK_SUCCESS)
223 {
224 instanceVersion = VK_API_VERSION_1_0;
225 }
226 #endif // VK_VERSION_1_1
227
228 std::vector<std::string> availableInstanceExtensions = GetInstanceExtensionNames();
229 std::vector<const char *> enabledInstanceExtensions;
230
231 bool hasPortabilityEnumeration = false;
232
233 if (IsApple() && ExtensionFound(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
234 availableInstanceExtensions))
235 {
236 // On iOS/macOS, there is no native Vulkan driver, so we need to
237 // enable the portability enumeration extension to allow use of
238 // MoltenVK.
239 enabledInstanceExtensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
240 hasPortabilityEnumeration = true;
241 }
242
243 // Create a Vulkan instance:
244 VkApplicationInfo appInfo;
245 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
246 appInfo.pNext = nullptr;
247 appInfo.pApplicationName = "";
248 appInfo.applicationVersion = 1;
249 appInfo.pEngineName = "";
250 appInfo.engineVersion = 1;
251 appInfo.apiVersion = instanceVersion;
252
253 VkInstanceCreateInfo createInstanceInfo;
254 createInstanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
255 createInstanceInfo.pNext = nullptr;
256 createInstanceInfo.flags = 0;
257 createInstanceInfo.pApplicationInfo = &appInfo;
258 createInstanceInfo.enabledLayerCount = 0;
259 createInstanceInfo.ppEnabledLayerNames = nullptr;
260 createInstanceInfo.enabledExtensionCount =
261 static_cast<uint32_t>(enabledInstanceExtensions.size());
262 createInstanceInfo.ppEnabledExtensionNames =
263 enabledInstanceExtensions.empty() ? nullptr : enabledInstanceExtensions.data();
264
265 if (hasPortabilityEnumeration)
266 {
267 createInstanceInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
268 }
269
270 auto pfnCreateInstance = getProc<PFN_vkCreateInstance>("vkCreateInstance");
271 if (!pfnCreateInstance ||
272 pfnCreateInstance(&createInstanceInfo, nullptr, &mInstance) != VK_SUCCESS)
273 {
274 return VK_NULL_HANDLE;
275 }
276
277 return mInstance;
278 }
279
280 template <typename Func>
getProc(const char * fn) const281 Func getProc(const char *fn) const
282 {
283 return reinterpret_cast<Func>(angle::GetLibrarySymbol(mLibVulkan, fn));
284 }
285
286 private:
287 void *mLibVulkan = nullptr;
288 VkInstance mInstance = VK_NULL_HANDLE;
289 };
290
291 ANGLE_FORMAT_PRINTF(1, 2)
FormatString(const char * fmt,...)292 std::string FormatString(const char *fmt, ...)
293 {
294 va_list vararg;
295 va_start(vararg, fmt);
296
297 std::vector<char> buffer;
298 size_t len = FormatStringIntoVector(fmt, vararg, buffer);
299 va_end(vararg);
300
301 return std::string(&buffer[0], len);
302 }
303
GetSystemInfoVulkan(SystemInfo * info)304 bool GetSystemInfoVulkan(SystemInfo *info)
305 {
306 return GetSystemInfoVulkanWithICD(info, vk::ICD::Default);
307 }
308
GetSystemInfoVulkanWithICD(SystemInfo * info,vk::ICD preferredICD)309 bool GetSystemInfoVulkanWithICD(SystemInfo *info, vk::ICD preferredICD)
310 {
311 const bool enableValidationLayers = false;
312 vk::ScopedVkLoaderEnvironment scopedEnvironment(enableValidationLayers, preferredICD);
313
314 static_assert(sizeof(GPUDeviceInfo::deviceUUID) == VK_UUID_SIZE);
315
316 // This implementation builds on top of the Vulkan API, but cannot assume the existence of the
317 // Vulkan library. ANGLE can be installed on versions of Android as old as Ice Cream Sandwich.
318 // Therefore, we need to use dlopen()/dlsym() in order to see if Vulkan is installed on the
319 // system, and if so, to use it:
320 VulkanLibrary vkLibrary;
321 VkInstance instance = vkLibrary.getVulkanInstance();
322 if (instance == VK_NULL_HANDLE)
323 {
324 // If Vulkan doesn't exist, bail-out early:
325 return false;
326 }
327
328 // Enumerate the Vulkan physical devices, which are ANGLE gpus:
329 auto pfnEnumeratePhysicalDevices =
330 vkLibrary.getProc<PFN_vkEnumeratePhysicalDevices>("vkEnumeratePhysicalDevices");
331 auto pfnGetPhysicalDeviceProperties =
332 vkLibrary.getProc<PFN_vkGetPhysicalDeviceProperties>("vkGetPhysicalDeviceProperties");
333 auto pfnGetPhysicalDeviceProperties2 =
334 vkLibrary.getProc<PFN_vkGetPhysicalDeviceProperties2>("vkGetPhysicalDeviceProperties2");
335 uint32_t physicalDeviceCount = 0;
336 if (!pfnEnumeratePhysicalDevices || !pfnGetPhysicalDeviceProperties ||
337 pfnEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr) != VK_SUCCESS)
338 {
339 return false;
340 }
341 std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
342 if (pfnEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices.data()) !=
343 VK_SUCCESS)
344 {
345 return false;
346 }
347
348 // If we get to here, we will likely provide a valid answer (unless an unknown vendorID):
349 info->gpus.resize(physicalDeviceCount);
350
351 for (uint32_t i = 0; i < physicalDeviceCount; i++)
352 {
353 VkPhysicalDeviceDriverProperties driverProperties = {};
354 driverProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
355
356 VkPhysicalDeviceIDProperties deviceIDProperties = {};
357 deviceIDProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
358 deviceIDProperties.pNext = &driverProperties;
359
360 VkPhysicalDeviceProperties2 properties2 = {};
361 properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
362 properties2.pNext = &deviceIDProperties;
363
364 VkPhysicalDeviceProperties &properties = properties2.properties;
365 pfnGetPhysicalDeviceProperties(physicalDevices[i], &properties);
366
367 // vkGetPhysicalDeviceProperties2() is supported since 1.1
368 // Use vkGetPhysicalDeviceProperties2() to get driver information.
369 if (properties.apiVersion >= VK_API_VERSION_1_1)
370 {
371 pfnGetPhysicalDeviceProperties2(physicalDevices[i], &properties2);
372 }
373
374 // Fill in data for a given physical device (a.k.a. gpu):
375 GPUDeviceInfo &gpu = info->gpus[i];
376 gpu.vendorId = properties.vendorID;
377 gpu.deviceId = properties.deviceID;
378 memcpy(gpu.deviceUUID, deviceIDProperties.deviceUUID, VK_UUID_SIZE);
379 memcpy(gpu.driverUUID, deviceIDProperties.driverUUID, VK_UUID_SIZE);
380
381 // TODO(http://anglebug.com/42266143): Use driverID instead of the hardware vendorID to
382 // detect driveVendor, etc.
383 switch (properties.vendorID)
384 {
385 case kVendorID_AMD:
386 gpu.driverVendor = "Advanced Micro Devices, Inc";
387 gpu.detailedDriverVersion = ParseAMDVulkanDriverVersion(properties.driverVersion);
388 break;
389 case kVendorID_ARM:
390 gpu.driverVendor = "Arm Holdings";
391 gpu.detailedDriverVersion = ParseArmVulkanDriverVersion(properties.driverVersion);
392 break;
393 case kVendorID_Broadcom:
394 gpu.driverVendor = "Broadcom";
395 gpu.detailedDriverVersion =
396 ParseBroadcomVulkanDriverVersion(properties.driverVersion);
397 break;
398 case kVendorID_GOOGLE:
399 gpu.driverVendor = "Google";
400 gpu.detailedDriverVersion =
401 ParseSwiftShaderVulkanDriverVersion(properties.driverVersion);
402 break;
403 case kVendorID_ImgTec:
404 gpu.driverVendor = "Imagination Technologies Limited";
405 gpu.detailedDriverVersion =
406 ParseImaginationVulkanDriverVersion(properties.driverVersion);
407 break;
408 case kVendorID_Intel:
409 gpu.driverVendor = "Intel Corporation";
410 if (IsWindows())
411 {
412 gpu.detailedDriverVersion =
413 ParseIntelWindowsVulkanDriverVersion(properties.driverVersion);
414 }
415 else
416 {
417 gpu.detailedDriverVersion =
418 ParseMesaVulkanDriverVersion(properties.driverVersion);
419 }
420 break;
421 case kVendorID_Kazan:
422 gpu.driverVendor = "Kazan Software";
423 gpu.detailedDriverVersion = ParseKazanVulkanDriverVersion(properties.driverVersion);
424 break;
425 case kVendorID_NVIDIA:
426 gpu.driverVendor = "NVIDIA Corporation";
427 gpu.detailedDriverVersion =
428 ParseNvidiaVulkanDriverVersion(properties.driverVersion);
429 break;
430 case kVendorID_Qualcomm:
431 case kVendorID_Qualcomm_DXGI:
432 gpu.driverVendor = "Qualcomm Technologies, Inc";
433 gpu.detailedDriverVersion =
434 ParseQualcommVulkanDriverVersion(properties.driverVersion);
435 break;
436 case kVendorID_Samsung:
437 gpu.driverVendor = "Samsung";
438 gpu.detailedDriverVersion =
439 ParseSamsungVulkanDriverVersion(properties.driverVersion);
440 break;
441 case kVendorID_VeriSilicon:
442 gpu.driverVendor = "VeriSilicon";
443 gpu.detailedDriverVersion =
444 ParseVeriSiliconVulkanDriverVersion(properties.driverVersion);
445 break;
446 case kVendorID_Vivante:
447 gpu.driverVendor = "Vivante";
448 gpu.detailedDriverVersion =
449 ParseVivanteVulkanDriverVersion(properties.driverVersion);
450 break;
451 case kVendorID_Mesa:
452 gpu.driverVendor = "Mesa";
453 gpu.detailedDriverVersion = ParseMesaVulkanDriverVersion(properties.driverVersion);
454 break;
455 case kVendorID_Apple:
456 // Note: This is MoltenVk
457 gpu.driverVendor = "Apple";
458 gpu.detailedDriverVersion =
459 ParseMoltenVulkanDriverVersion(properties.driverVersion);
460 break;
461 default:
462 return false;
463 }
464 gpu.driverVersion =
465 FormatString("%d.%d.%d", gpu.detailedDriverVersion.major,
466 gpu.detailedDriverVersion.minor, gpu.detailedDriverVersion.subMinor);
467 gpu.driverId = static_cast<DriverID>(driverProperties.driverID);
468 gpu.driverApiVersion = properties.apiVersion;
469 gpu.driverDate = "";
470 }
471
472 return true;
473 }
474
475 } // namespace angle
476