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 
9 #include <dlfcn.h>
10 #include <vulkan/vulkan.h>
11 #include "gpu_info_util/SystemInfo_internal.h"
12 
13 #include <cstring>
14 #include <fstream>
15 
16 #include "common/angleutils.h"
17 #include "common/debug.h"
18 
19 namespace angle
20 {
21 class VulkanLibrary final : NonCopyable
22 {
23   public:
VulkanLibrary()24     VulkanLibrary() {}
~VulkanLibrary()25     ~VulkanLibrary()
26     {
27         if (mInstance != VK_NULL_HANDLE)
28         {
29             PFN_vkDestroyInstance pfnDestroyInstance =
30                 reinterpret_cast<PFN_vkDestroyInstance>(dlsym(mLibVulkan, "vkDestroyInstance"));
31             if (pfnDestroyInstance)
32             {
33                 pfnDestroyInstance(mInstance, nullptr);
34             }
35         }
36         if (mLibVulkan)
37             dlclose(mLibVulkan);
38     }
getVulkanInstance()39     VkInstance getVulkanInstance()
40     {
41         // Find the system's Vulkan library and open it:
42         mLibVulkan = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
43         if (!mLibVulkan)
44         {
45             // If Vulkan doesn't exist, bail-out early:
46             return VK_NULL_HANDLE;
47         }
48 
49         // Determine the available Vulkan instance version:
50         uint32_t instanceVersion = VK_API_VERSION_1_0;
51 #if defined(VK_VERSION_1_1)
52         PFN_vkEnumerateInstanceVersion pfnEnumerateInstanceVersion =
53             reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
54                 dlsym(mLibVulkan, "vkEnumerateInstanceVersion"));
55         if (!pfnEnumerateInstanceVersion ||
56             pfnEnumerateInstanceVersion(&instanceVersion) != VK_SUCCESS)
57         {
58             instanceVersion = VK_API_VERSION_1_0;
59         }
60 #endif  // VK_VERSION_1_1
61 
62         // Create a Vulkan instance:
63         VkApplicationInfo appInfo;
64         appInfo.sType              = VK_STRUCTURE_TYPE_APPLICATION_INFO;
65         appInfo.pNext              = nullptr;
66         appInfo.pApplicationName   = "";
67         appInfo.applicationVersion = 1;
68         appInfo.pEngineName        = "";
69         appInfo.engineVersion      = 1;
70         appInfo.apiVersion         = instanceVersion;
71 
72         VkInstanceCreateInfo createInstanceInfo;
73         createInstanceInfo.sType                   = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
74         createInstanceInfo.pNext                   = nullptr;
75         createInstanceInfo.flags                   = 0;
76         createInstanceInfo.pApplicationInfo        = &appInfo;
77         createInstanceInfo.enabledLayerCount       = 0;
78         createInstanceInfo.ppEnabledLayerNames     = nullptr;
79         createInstanceInfo.enabledExtensionCount   = 0;
80         createInstanceInfo.ppEnabledExtensionNames = nullptr;
81 
82         PFN_vkCreateInstance pfnCreateInstance =
83             reinterpret_cast<PFN_vkCreateInstance>(dlsym(mLibVulkan, "vkCreateInstance"));
84         if (!pfnCreateInstance ||
85             pfnCreateInstance(&createInstanceInfo, nullptr, &mInstance) != VK_SUCCESS)
86         {
87             return VK_NULL_HANDLE;
88         }
89 
90         return mInstance;
91     }
gpa(std::string fn)92     void *gpa(std::string fn) { return dlsym(mLibVulkan, fn.c_str()); }
93 #define GPA(ob, type, fn) reinterpret_cast<type>(ob.gpa(fn))
94 
95   private:
96     void *mLibVulkan     = nullptr;
97     VkInstance mInstance = VK_NULL_HANDLE;
98 };
99 
100 ANGLE_FORMAT_PRINTF(1, 2)
FormatString(const char * fmt,...)101 std::string FormatString(const char *fmt, ...)
102 {
103     va_list vararg;
104     va_start(vararg, fmt);
105 
106     std::vector<char> buffer(512);
107     size_t len = FormatStringIntoVector(fmt, vararg, buffer);
108     va_end(vararg);
109 
110     return std::string(&buffer[0], len);
111 }
112 
GetSystemInfoVulkan(SystemInfo * info)113 bool GetSystemInfoVulkan(SystemInfo *info)
114 {
115     // This implementation builds on top of the Vulkan API, but cannot assume the existence of the
116     // Vulkan library.  ANGLE can be installed on versions of Android as old as Ice Cream Sandwich.
117     // Therefore, we need to use dlopen()/dlsym() in order to see if Vulkan is installed on the
118     // system, and if so, to use it:
119     VulkanLibrary vkLibrary;
120     VkInstance instance = vkLibrary.getVulkanInstance();
121     if (instance == VK_NULL_HANDLE)
122     {
123         // If Vulkan doesn't exist, bail-out early:
124         return false;
125     }
126 
127     // Enumerate the Vulkan physical devices, which are ANGLE gpus:
128     PFN_vkEnumeratePhysicalDevices pfnEnumeratePhysicalDevices =
129         GPA(vkLibrary, PFN_vkEnumeratePhysicalDevices, "vkEnumeratePhysicalDevices");
130     PFN_vkGetPhysicalDeviceProperties pfnGetPhysicalDeviceProperties =
131         GPA(vkLibrary, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");
132     uint32_t physicalDeviceCount = 0;
133     if (!pfnEnumeratePhysicalDevices ||
134         pfnEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr) != VK_SUCCESS)
135     {
136         return false;
137     }
138     std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
139     if (pfnEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices.data()) !=
140         VK_SUCCESS)
141     {
142         return false;
143     }
144 
145     // If we get to here, we will likely provide a valid answer (unless an unknown vendorID):
146     info->gpus.resize(physicalDeviceCount);
147 
148     for (uint32_t i = 0; i < physicalDeviceCount; i++)
149     {
150         VkPhysicalDeviceProperties properties;
151         pfnGetPhysicalDeviceProperties(physicalDevices[i], &properties);
152         // Fill in data for a given physical device (a.k.a. gpu):
153         GPUDeviceInfo &gpu = info->gpus[i];
154         gpu.vendorId       = properties.vendorID;
155         gpu.deviceId       = properties.deviceID;
156         // Need to parse/re-format properties.driverVersion.
157         //
158         // TODO(ianelliott): Determine the formatting used for each vendor
159         // (http://anglebug.com/2677)
160         switch (properties.vendorID)
161         {
162             case kVendorID_AMD:
163                 gpu.driverVendor                = "Advanced Micro Devices, Inc";
164                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
165                 gpu.detailedDriverVersion.major = properties.driverVersion;
166                 break;
167             case kVendorID_ARM:
168                 gpu.driverVendor                = "Arm Holdings";
169                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
170                 gpu.detailedDriverVersion.major = properties.driverVersion;
171                 break;
172             case kVendorID_Broadcom:
173                 gpu.driverVendor                = "Broadcom";
174                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
175                 gpu.detailedDriverVersion.major = properties.driverVersion;
176                 break;
177             case kVendorID_ImgTec:
178                 gpu.driverVendor                = "Imagination Technologies Limited";
179                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
180                 gpu.detailedDriverVersion.major = properties.driverVersion;
181                 break;
182             case kVendorID_Intel:
183                 gpu.driverVendor                = "Intel Corporation";
184                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
185                 gpu.detailedDriverVersion.major = properties.driverVersion;
186                 break;
187             case kVendorID_Kazan:
188                 gpu.driverVendor                = "Kazan Software";
189                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
190                 gpu.detailedDriverVersion.major = properties.driverVersion;
191                 break;
192             case kVendorID_NVIDIA:
193                 gpu.driverVendor  = "NVIDIA Corporation";
194                 gpu.driverVersion = FormatString("%d.%d.%d.%d", properties.driverVersion >> 22,
195                                                  (properties.driverVersion >> 14) & 0XFF,
196                                                  (properties.driverVersion >> 6) & 0XFF,
197                                                  properties.driverVersion & 0x3F);
198                 gpu.detailedDriverVersion.major    = properties.driverVersion >> 22;
199                 gpu.detailedDriverVersion.minor    = (properties.driverVersion >> 14) & 0xFF;
200                 gpu.detailedDriverVersion.subMinor = (properties.driverVersion >> 6) & 0xFF;
201                 gpu.detailedDriverVersion.patch    = properties.driverVersion & 0x3F;
202                 break;
203             case kVendorID_Qualcomm:
204                 gpu.driverVendor = "Qualcomm Technologies, Inc";
205                 if (properties.driverVersion & 0x80000000)
206                 {
207                     gpu.driverVersion = FormatString("%d.%d.%d", properties.driverVersion >> 22,
208                                                      (properties.driverVersion >> 12) & 0X3FF,
209                                                      properties.driverVersion & 0xFFF);
210                     gpu.detailedDriverVersion.major    = properties.driverVersion >> 22;
211                     gpu.detailedDriverVersion.minor    = (properties.driverVersion >> 12) & 0x3FF;
212                     gpu.detailedDriverVersion.subMinor = properties.driverVersion & 0xFFF;
213                 }
214                 else
215                 {
216                     gpu.driverVersion = FormatString("0x%x", properties.driverVersion);
217                     gpu.detailedDriverVersion.major = properties.driverVersion;
218                 }
219                 break;
220             case kVendorID_VeriSilicon:
221                 gpu.driverVendor                = "VeriSilicon";
222                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
223                 gpu.detailedDriverVersion.major = properties.driverVersion;
224                 break;
225             case kVendorID_Vivante:
226                 gpu.driverVendor                = "Vivante";
227                 gpu.driverVersion               = FormatString("0x%x", properties.driverVersion);
228                 gpu.detailedDriverVersion.major = properties.driverVersion;
229                 break;
230             default:
231                 return false;
232         }
233         gpu.driverDate = "";
234     }
235 
236     return true;
237 }
238 
239 }  // namespace angle
240