• 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/5103
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 class VulkanLibrary final : NonCopyable
30 {
31   public:
32     VulkanLibrary() = default;
33 
~VulkanLibrary()34     ~VulkanLibrary()
35     {
36         if (mInstance != VK_NULL_HANDLE)
37         {
38             auto pfnDestroyInstance = getProc<PFN_vkDestroyInstance>("vkDestroyInstance");
39             if (pfnDestroyInstance)
40             {
41                 pfnDestroyInstance(mInstance, nullptr);
42             }
43         }
44 
45         CloseSystemLibrary(mLibVulkan);
46     }
47 
GetInstanceExtensionNames() const48     std::vector<std::string> GetInstanceExtensionNames() const
49     {
50         std::vector<std::string> extensionNames;
51 
52         auto pfnEnumerateInstanceExtensionProperties =
53             getProc<PFN_vkEnumerateInstanceExtensionProperties>(
54                 "vkEnumerateInstanceExtensionProperties");
55         if (!pfnEnumerateInstanceExtensionProperties)
56         {
57             return extensionNames;
58         }
59 
60         uint32_t extensionCount = 0;
61         if (pfnEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr) !=
62             VK_SUCCESS)
63         {
64             return extensionNames;
65         }
66 
67         std::vector<VkExtensionProperties> extensions(extensionCount);
68         if (pfnEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()) !=
69             VK_SUCCESS)
70         {
71             return extensionNames;
72         }
73 
74         for (const auto &extension : extensions)
75         {
76             extensionNames.emplace_back(extension.extensionName);
77         }
78 
79         std::sort(extensionNames.begin(), extensionNames.end());
80 
81         return extensionNames;
82     }
83 
ExtensionFound(std::string const & needle,const std::vector<std::string> & haystack)84     bool ExtensionFound(std::string const &needle, const std::vector<std::string> &haystack)
85     {
86         // NOTE: The list must be sorted.
87         return std::binary_search(haystack.begin(), haystack.end(), needle);
88     }
89 
getVulkanInstance()90     VkInstance getVulkanInstance()
91     {
92         mLibVulkan = vk::OpenLibVulkan();
93         if (!mLibVulkan)
94         {
95             // If Vulkan doesn't exist, bail-out early:
96             return VK_NULL_HANDLE;
97         }
98 
99         // Determine the available Vulkan instance version:
100         uint32_t instanceVersion = VK_API_VERSION_1_0;
101 #if defined(VK_VERSION_1_1)
102         auto pfnEnumerateInstanceVersion =
103             getProc<PFN_vkEnumerateInstanceVersion>("vkEnumerateInstanceVersion");
104         if (!pfnEnumerateInstanceVersion ||
105             pfnEnumerateInstanceVersion(&instanceVersion) != VK_SUCCESS)
106         {
107             instanceVersion = VK_API_VERSION_1_0;
108         }
109 #endif  // VK_VERSION_1_1
110 
111         std::vector<std::string> availableInstanceExtensions = GetInstanceExtensionNames();
112         std::vector<const char *> enabledInstanceExtensions;
113 
114         bool hasPortabilityEnumeration = false;
115 
116         if (IsApple() && ExtensionFound(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
117                                         availableInstanceExtensions))
118         {
119             // On iOS/macOS, there is no native Vulkan driver, so we need to
120             // enable the portability enumeration extension to allow use of
121             // MoltenVK.
122             enabledInstanceExtensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
123             hasPortabilityEnumeration = true;
124         }
125 
126         // Create a Vulkan instance:
127         VkApplicationInfo appInfo;
128         appInfo.sType              = VK_STRUCTURE_TYPE_APPLICATION_INFO;
129         appInfo.pNext              = nullptr;
130         appInfo.pApplicationName   = "";
131         appInfo.applicationVersion = 1;
132         appInfo.pEngineName        = "";
133         appInfo.engineVersion      = 1;
134         appInfo.apiVersion         = instanceVersion;
135 
136         VkInstanceCreateInfo createInstanceInfo;
137         createInstanceInfo.sType               = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
138         createInstanceInfo.pNext               = nullptr;
139         createInstanceInfo.flags               = 0;
140         createInstanceInfo.pApplicationInfo    = &appInfo;
141         createInstanceInfo.enabledLayerCount   = 0;
142         createInstanceInfo.ppEnabledLayerNames = nullptr;
143         createInstanceInfo.enabledExtensionCount =
144             static_cast<uint32_t>(enabledInstanceExtensions.size());
145         createInstanceInfo.ppEnabledExtensionNames =
146             enabledInstanceExtensions.empty() ? nullptr : enabledInstanceExtensions.data();
147 
148         if (hasPortabilityEnumeration)
149         {
150             createInstanceInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
151         }
152 
153         auto pfnCreateInstance = getProc<PFN_vkCreateInstance>("vkCreateInstance");
154         if (!pfnCreateInstance ||
155             pfnCreateInstance(&createInstanceInfo, nullptr, &mInstance) != VK_SUCCESS)
156         {
157             return VK_NULL_HANDLE;
158         }
159 
160         return mInstance;
161     }
162 
163     template <typename Func>
getProc(const char * fn) const164     Func getProc(const char *fn) const
165     {
166         return reinterpret_cast<Func>(angle::GetLibrarySymbol(mLibVulkan, fn));
167     }
168 
169   private:
170     void *mLibVulkan     = nullptr;
171     VkInstance mInstance = VK_NULL_HANDLE;
172 };
173 
174 ANGLE_FORMAT_PRINTF(1, 2)
FormatString(const char * fmt,...)175 std::string FormatString(const char *fmt, ...)
176 {
177     va_list vararg;
178     va_start(vararg, fmt);
179 
180     std::vector<char> buffer;
181     size_t len = FormatStringIntoVector(fmt, vararg, buffer);
182     va_end(vararg);
183 
184     return std::string(&buffer[0], len);
185 }
186 
GetSystemInfoVulkan(SystemInfo * info)187 bool GetSystemInfoVulkan(SystemInfo *info)
188 {
189     return GetSystemInfoVulkanWithICD(info, vk::ICD::Default);
190 }
191 
GetSystemInfoVulkanWithICD(SystemInfo * info,vk::ICD preferredICD)192 bool GetSystemInfoVulkanWithICD(SystemInfo *info, vk::ICD preferredICD)
193 {
194     const bool enableValidationLayers = false;
195     vk::ScopedVkLoaderEnvironment scopedEnvironment(enableValidationLayers, preferredICD);
196 
197     // This implementation builds on top of the Vulkan API, but cannot assume the existence of the
198     // Vulkan library.  ANGLE can be installed on versions of Android as old as Ice Cream Sandwich.
199     // Therefore, we need to use dlopen()/dlsym() in order to see if Vulkan is installed on the
200     // system, and if so, to use it:
201     VulkanLibrary vkLibrary;
202     VkInstance instance = vkLibrary.getVulkanInstance();
203     if (instance == VK_NULL_HANDLE)
204     {
205         // If Vulkan doesn't exist, bail-out early:
206         return false;
207     }
208 
209     // Enumerate the Vulkan physical devices, which are ANGLE gpus:
210     auto pfnEnumeratePhysicalDevices =
211         vkLibrary.getProc<PFN_vkEnumeratePhysicalDevices>("vkEnumeratePhysicalDevices");
212     auto pfnGetPhysicalDeviceProperties =
213         vkLibrary.getProc<PFN_vkGetPhysicalDeviceProperties>("vkGetPhysicalDeviceProperties");
214     auto pfnGetPhysicalDeviceProperties2 =
215         vkLibrary.getProc<PFN_vkGetPhysicalDeviceProperties2>("vkGetPhysicalDeviceProperties2");
216     uint32_t physicalDeviceCount = 0;
217     if (!pfnEnumeratePhysicalDevices || !pfnGetPhysicalDeviceProperties ||
218         pfnEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr) != VK_SUCCESS)
219     {
220         return false;
221     }
222     std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
223     if (pfnEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices.data()) !=
224         VK_SUCCESS)
225     {
226         return false;
227     }
228 
229     // If we get to here, we will likely provide a valid answer (unless an unknown vendorID):
230     info->gpus.resize(physicalDeviceCount);
231 
232     for (uint32_t i = 0; i < physicalDeviceCount; i++)
233     {
234         VkPhysicalDeviceDriverProperties driverProperties = {};
235         driverProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
236 
237         VkPhysicalDeviceProperties2 properties2 = {};
238         properties2.sType                       = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
239         properties2.pNext                       = &driverProperties;
240 
241         VkPhysicalDeviceProperties &properties = properties2.properties;
242         pfnGetPhysicalDeviceProperties(physicalDevices[i], &properties);
243 
244         // vkGetPhysicalDeviceProperties2() is supported since 1.1
245         // Use vkGetPhysicalDeviceProperties2() to get driver information.
246         if (properties.apiVersion >= VK_API_VERSION_1_1)
247         {
248             pfnGetPhysicalDeviceProperties2(physicalDevices[i], &properties2);
249         }
250 
251         // Fill in data for a given physical device (a.k.a. gpu):
252         GPUDeviceInfo &gpu = info->gpus[i];
253         gpu.vendorId       = properties.vendorID;
254         gpu.deviceId       = properties.deviceID;
255         // Need to parse/re-format properties.driverVersion.
256         //
257         // TODO(ianelliott): Determine the formatting used for each vendor
258         // (http://anglebug.com/2677)
259         // TODO(http://anglebug.com/7677): Use driverID instead of the hardware vendorID to detect
260         // driveVendor, etc.
261         switch (properties.vendorID)
262         {
263             case kVendorID_AMD:
264                 gpu.driverVendor                = "Advanced Micro Devices, Inc";
265                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
266                 gpu.detailedDriverVersion.major = properties.driverVersion;
267                 break;
268             case kVendorID_ARM:
269                 gpu.driverVendor                = "Arm Holdings";
270                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
271                 gpu.detailedDriverVersion.major = properties.driverVersion;
272                 break;
273             case kVendorID_Broadcom:
274                 gpu.driverVendor                = "Broadcom";
275                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
276                 gpu.detailedDriverVersion.major = properties.driverVersion;
277                 break;
278             case kVendorID_GOOGLE:
279                 gpu.driverVendor                = "Google";
280                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
281                 gpu.detailedDriverVersion.major = properties.driverVersion;
282                 break;
283             case kVendorID_ImgTec:
284                 gpu.driverVendor                = "Imagination Technologies Limited";
285                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
286                 gpu.detailedDriverVersion.major = properties.driverVersion;
287                 break;
288             case kVendorID_Intel:
289                 gpu.driverVendor                = "Intel Corporation";
290                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
291                 gpu.detailedDriverVersion.major = properties.driverVersion;
292                 break;
293             case kVendorID_Kazan:
294                 gpu.driverVendor                = "Kazan Software";
295                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
296                 gpu.detailedDriverVersion.major = properties.driverVersion;
297                 break;
298             case kVendorID_NVIDIA:
299                 gpu.driverVendor  = "NVIDIA Corporation";
300                 gpu.driverVersion = FormatString("%d.%d.%d.%d", properties.driverVersion >> 22,
301                                                  (properties.driverVersion >> 14) & 0XFF,
302                                                  (properties.driverVersion >> 6) & 0XFF,
303                                                  properties.driverVersion & 0x3F);
304                 gpu.detailedDriverVersion.major    = properties.driverVersion >> 22;
305                 gpu.detailedDriverVersion.minor    = (properties.driverVersion >> 14) & 0xFF;
306                 gpu.detailedDriverVersion.subMinor = (properties.driverVersion >> 6) & 0xFF;
307                 gpu.detailedDriverVersion.patch    = properties.driverVersion & 0x3F;
308                 break;
309             case kVendorID_Qualcomm:
310                 gpu.driverVendor = "Qualcomm Technologies, Inc";
311                 if (properties.driverVersion & 0x80000000)
312                 {
313                     gpu.driverVersion = FormatString("%d.%d.%d", properties.driverVersion >> 22,
314                                                      (properties.driverVersion >> 12) & 0X3FF,
315                                                      properties.driverVersion & 0xFFF);
316                     gpu.detailedDriverVersion.major    = properties.driverVersion >> 22;
317                     gpu.detailedDriverVersion.minor    = (properties.driverVersion >> 12) & 0x3FF;
318                     gpu.detailedDriverVersion.subMinor = properties.driverVersion & 0xFFF;
319                 }
320                 else
321                 {
322                     gpu.driverVersion = FormatString("0x%x", properties.driverVersion);
323                     gpu.detailedDriverVersion.major = properties.driverVersion;
324                 }
325                 break;
326             case kVendorID_Samsung:
327                 gpu.driverVendor                = "Samsung";
328                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
329                 gpu.detailedDriverVersion.major = properties.driverVersion;
330                 break;
331             case kVendorID_VeriSilicon:
332                 gpu.driverVendor                = "VeriSilicon";
333                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
334                 gpu.detailedDriverVersion.major = properties.driverVersion;
335                 break;
336             case kVendorID_Vivante:
337                 gpu.driverVendor                = "Vivante";
338                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
339                 gpu.detailedDriverVersion.major = properties.driverVersion;
340                 break;
341             case kVendorID_Mesa:
342                 gpu.driverVendor                = "Mesa";
343                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
344                 gpu.detailedDriverVersion.major = properties.driverVersion;
345                 break;
346             case kVendorID_Apple:
347                 // Note: this is the version extraction for MoltenVK, which
348                 // formulates its version number as a decimal number like so:
349                 //     (major * 10000) + (minor * 100) + patch
350                 gpu.driverVendor                = "Apple";
351                 gpu.detailedDriverVersion.major = properties.driverVersion / 10000;
352                 gpu.detailedDriverVersion.minor = (properties.driverVersion / 100) % 100;
353                 gpu.detailedDriverVersion.patch = properties.driverVersion % 100;
354                 gpu.driverVersion =
355                     FormatString("%d.%d.%d", gpu.detailedDriverVersion.major,
356                                  gpu.detailedDriverVersion.minor, gpu.detailedDriverVersion.patch);
357                 break;
358             default:
359                 return false;
360         }
361         gpu.driverId         = static_cast<DriverID>(driverProperties.driverID);
362         gpu.driverApiVersion = properties.apiVersion;
363         gpu.driverDate       = "";
364     }
365 
366     return true;
367 }
368 
369 }  // namespace angle
370