• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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