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