• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2013 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.cpp: implementation of the system-agnostic parts of SystemInfo.h
8 
9 #include "gpu_info_util/SystemInfo.h"
10 
11 #include <cstring>
12 #include <iostream>
13 #include <sstream>
14 
15 #include "anglebase/no_destructor.h"
16 #include "common/debug.h"
17 #include "common/string_utils.h"
18 #include "common/system_utils.h"
19 
20 namespace angle
21 {
22 namespace
23 {
24 constexpr char kANGLEPreferredDeviceEnv[] = "ANGLE_PREFERRED_DEVICE";
25 }
26 
VendorName(VendorID vendor)27 std::string VendorName(VendorID vendor)
28 {
29     switch (vendor)
30     {
31         case kVendorID_AMD:
32             return "AMD";
33         case kVendorID_ARM:
34             return "ARM";
35         case kVendorID_Broadcom:
36             return "Broadcom";
37         case kVendorID_GOOGLE:
38             return "Google";
39         case kVendorID_ImgTec:
40             return "ImgTec";
41         case kVendorID_Intel:
42             return "Intel";
43         case kVendorID_Kazan:
44             return "Kazan";
45         case kVendorID_NVIDIA:
46             return "NVIDIA";
47         case kVendorID_Qualcomm:
48             return "Qualcomm";
49         case kVendorID_VeriSilicon:
50             return "VeriSilicon";
51         case kVendorID_Vivante:
52             return "Vivante";
53         case kVendorID_VMWare:
54             return "VMWare";
55         case kVendorID_Apple:
56             return "Apple";
57         case kVendorID_Microsoft:
58             return "Microsoft";
59         default:
60             return "Unknown (" + std::to_string(vendor) + ")";
61     }
62 }
63 
64 GPUDeviceInfo::GPUDeviceInfo() = default;
65 
66 GPUDeviceInfo::~GPUDeviceInfo() = default;
67 
68 GPUDeviceInfo::GPUDeviceInfo(const GPUDeviceInfo &other) = default;
69 
70 SystemInfo::SystemInfo() = default;
71 
72 SystemInfo::~SystemInfo() = default;
73 
74 SystemInfo::SystemInfo(const SystemInfo &other) = default;
75 
hasNVIDIAGPU() const76 bool SystemInfo::hasNVIDIAGPU() const
77 {
78     for (const GPUDeviceInfo &gpu : gpus)
79     {
80         if (IsNVIDIA(gpu.vendorId))
81         {
82             return true;
83         }
84     }
85     return false;
86 }
87 
hasIntelGPU() const88 bool SystemInfo::hasIntelGPU() const
89 {
90     for (const GPUDeviceInfo &gpu : gpus)
91     {
92         if (IsIntel(gpu.vendorId))
93         {
94             return true;
95         }
96     }
97     return false;
98 }
99 
hasAMDGPU() const100 bool SystemInfo::hasAMDGPU() const
101 {
102     for (const GPUDeviceInfo &gpu : gpus)
103     {
104         if (IsAMD(gpu.vendorId))
105         {
106             return true;
107         }
108     }
109     return false;
110 }
111 
getPreferredGPUIndex() const112 std::optional<size_t> SystemInfo::getPreferredGPUIndex() const
113 {
114     std::string device = GetPreferredDeviceString();
115     if (!device.empty())
116     {
117         for (size_t i = 0; i < gpus.size(); ++i)
118         {
119             std::string vendor = VendorName(gpus[i].vendorId);
120             ToLower(&vendor);
121             if (vendor == device)
122                 return i;
123         }
124     }
125     return std::nullopt;
126 }
127 
IsAMD(VendorID vendorId)128 bool IsAMD(VendorID vendorId)
129 {
130     return vendorId == kVendorID_AMD;
131 }
132 
IsARM(VendorID vendorId)133 bool IsARM(VendorID vendorId)
134 {
135     return vendorId == kVendorID_ARM;
136 }
137 
IsBroadcom(VendorID vendorId)138 bool IsBroadcom(VendorID vendorId)
139 {
140     return vendorId == kVendorID_Broadcom;
141 }
142 
IsImgTec(VendorID vendorId)143 bool IsImgTec(VendorID vendorId)
144 {
145     return vendorId == kVendorID_ImgTec;
146 }
147 
IsKazan(VendorID vendorId)148 bool IsKazan(VendorID vendorId)
149 {
150     return vendorId == kVendorID_Kazan;
151 }
152 
IsIntel(VendorID vendorId)153 bool IsIntel(VendorID vendorId)
154 {
155     return vendorId == kVendorID_Intel;
156 }
157 
IsNVIDIA(VendorID vendorId)158 bool IsNVIDIA(VendorID vendorId)
159 {
160     return vendorId == kVendorID_NVIDIA;
161 }
162 
IsQualcomm(VendorID vendorId)163 bool IsQualcomm(VendorID vendorId)
164 {
165     return vendorId == kVendorID_Qualcomm;
166 }
167 
IsGoogle(VendorID vendorId)168 bool IsGoogle(VendorID vendorId)
169 {
170     return vendorId == kVendorID_GOOGLE;
171 }
172 
IsVeriSilicon(VendorID vendorId)173 bool IsVeriSilicon(VendorID vendorId)
174 {
175     return vendorId == kVendorID_VeriSilicon;
176 }
177 
IsVMWare(VendorID vendorId)178 bool IsVMWare(VendorID vendorId)
179 {
180     return vendorId == kVendorID_VMWare;
181 }
182 
IsVivante(VendorID vendorId)183 bool IsVivante(VendorID vendorId)
184 {
185     return vendorId == kVendorID_Vivante;
186 }
187 
IsApple(VendorID vendorId)188 bool IsApple(VendorID vendorId)
189 {
190     return vendorId == kVendorID_Apple;
191 }
192 
IsMicrosoft(VendorID vendorId)193 bool IsMicrosoft(VendorID vendorId)
194 {
195     return vendorId == kVendorID_Microsoft;
196 }
197 
ParseAMDBrahmaDriverVersion(const std::string & content,std::string * version)198 bool ParseAMDBrahmaDriverVersion(const std::string &content, std::string *version)
199 {
200     const size_t begin = content.find_first_of("0123456789");
201     if (begin == std::string::npos)
202     {
203         return false;
204     }
205 
206     const size_t end = content.find_first_not_of("0123456789.", begin);
207     if (end == std::string::npos)
208     {
209         *version = content.substr(begin);
210     }
211     else
212     {
213         *version = content.substr(begin, end - begin);
214     }
215     return true;
216 }
217 
ParseAMDCatalystDriverVersion(const std::string & content,std::string * version)218 bool ParseAMDCatalystDriverVersion(const std::string &content, std::string *version)
219 {
220     std::istringstream stream(content);
221 
222     std::string line;
223     while (std::getline(stream, line))
224     {
225         static const char kReleaseVersion[] = "ReleaseVersion=";
226         if (line.compare(0, std::strlen(kReleaseVersion), kReleaseVersion) != 0)
227         {
228             continue;
229         }
230 
231         if (ParseAMDBrahmaDriverVersion(line, version))
232         {
233             return true;
234         }
235     }
236     return false;
237 }
238 
ParseMacMachineModel(const std::string & identifier,std::string * type,int32_t * major,int32_t * minor)239 bool ParseMacMachineModel(const std::string &identifier,
240                           std::string *type,
241                           int32_t *major,
242                           int32_t *minor)
243 {
244     size_t numberLoc = identifier.find_first_of("0123456789");
245     if (numberLoc == std::string::npos)
246     {
247         return false;
248     }
249 
250     size_t commaLoc = identifier.find(',', numberLoc);
251     if (commaLoc == std::string::npos || commaLoc >= identifier.size())
252     {
253         return false;
254     }
255 
256     const char *numberPtr = &identifier[numberLoc];
257     const char *commaPtr  = &identifier[commaLoc + 1];
258     char *endPtr          = nullptr;
259 
260     int32_t majorTmp = static_cast<int32_t>(std::strtol(numberPtr, &endPtr, 10));
261     if (endPtr == numberPtr)
262     {
263         return false;
264     }
265 
266     int32_t minorTmp = static_cast<int32_t>(std::strtol(commaPtr, &endPtr, 10));
267     if (endPtr == commaPtr)
268     {
269         return false;
270     }
271 
272     *major = majorTmp;
273     *minor = minorTmp;
274     *type  = identifier.substr(0, numberLoc);
275 
276     return true;
277 }
278 
CMDeviceIDToDeviceAndVendorID(const std::string & id,uint32_t * vendorId,uint32_t * deviceId)279 bool CMDeviceIDToDeviceAndVendorID(const std::string &id, uint32_t *vendorId, uint32_t *deviceId)
280 {
281     unsigned int vendor = 0;
282     unsigned int device = 0;
283 
284     bool success = id.length() >= 21 && HexStringToUInt(id.substr(8, 4), &vendor) &&
285                    HexStringToUInt(id.substr(17, 4), &device);
286 
287     *vendorId = vendor;
288     *deviceId = device;
289     return success;
290 }
291 
GetDualGPUInfo(SystemInfo * info)292 void GetDualGPUInfo(SystemInfo *info)
293 {
294     ASSERT(!info->gpus.empty());
295 
296     // On dual-GPU systems we assume the non-Intel GPU is the graphics one.
297     // TODO: this is incorrect and problematic.  activeGPUIndex must be removed if it cannot be
298     // determined correctly.  A potential solution is to create an OpenGL context and parse
299     // GL_VENDOR.  Currently, our test infrastructure is relying on this information and incorrectly
300     // applies test expectations on dual-GPU systems when the Intel GPU is active.
301     // http://anglebug.com/6174.
302     int active    = 0;
303     bool hasIntel = false;
304     for (size_t i = 0; i < info->gpus.size(); ++i)
305     {
306         if (IsIntel(info->gpus[i].vendorId))
307         {
308             hasIntel = true;
309         }
310         if (IsIntel(info->gpus[active].vendorId))
311         {
312             active = static_cast<int>(i);
313         }
314     }
315 
316     // Assume that a combination of NVIDIA or AMD with Intel means Optimus or AMD Switchable
317     info->activeGPUIndex  = active;
318     info->isOptimus       = hasIntel && IsNVIDIA(info->gpus[active].vendorId);
319     info->isAMDSwitchable = hasIntel && IsAMD(info->gpus[active].vendorId);
320 }
321 
PrintSystemInfo(const SystemInfo & info)322 void PrintSystemInfo(const SystemInfo &info)
323 {
324     std::cout << info.gpus.size() << " GPUs:\n";
325 
326     for (size_t i = 0; i < info.gpus.size(); i++)
327     {
328         const auto &gpu = info.gpus[i];
329 
330         std::cout << "  " << i << " - " << VendorName(gpu.vendorId) << " device id: 0x" << std::hex
331                   << std::uppercase << gpu.deviceId << std::dec << ", revision id: 0x" << std::hex
332                   << std::uppercase << gpu.revisionId << std::dec << ", system device id: 0x"
333                   << std::hex << std::uppercase << gpu.systemDeviceId << std::dec << "\n";
334         if (!gpu.driverVendor.empty())
335         {
336             std::cout << "       Driver Vendor: " << gpu.driverVendor << "\n";
337         }
338         if (!gpu.driverVersion.empty())
339         {
340             std::cout << "       Driver Version: " << gpu.driverVersion << "\n";
341         }
342         if (!gpu.driverDate.empty())
343         {
344             std::cout << "       Driver Date: " << gpu.driverDate << "\n";
345         }
346         if (gpu.detailedDriverVersion.major != 0 || gpu.detailedDriverVersion.minor != 0 ||
347             gpu.detailedDriverVersion.subMinor != 0 || gpu.detailedDriverVersion.patch != 0)
348         {
349             std::cout << "       Detailed Driver Version:\n"
350                       << "           major: " << gpu.detailedDriverVersion.major
351                       << "           minor: " << gpu.detailedDriverVersion.minor
352                       << "           subMinor: " << gpu.detailedDriverVersion.subMinor
353                       << "           patch: " << gpu.detailedDriverVersion.patch << "\n";
354         }
355     }
356 
357     std::cout << "\n";
358     std::cout << "Active GPU: " << info.activeGPUIndex << "\n";
359 
360     std::cout << "\n";
361     std::cout << "Optimus: " << (info.isOptimus ? "true" : "false") << "\n";
362     std::cout << "AMD Switchable: " << (info.isAMDSwitchable ? "true" : "false") << "\n";
363     std::cout << "Mac Switchable: " << (info.isMacSwitchable ? "true" : "false") << "\n";
364     std::cout << "Needs EAGL on Mac: " << (info.needsEAGLOnMac ? "true" : "false") << "\n";
365 
366     std::cout << "\n";
367     if (!info.machineManufacturer.empty())
368     {
369         std::cout << "Machine Manufacturer: " << info.machineManufacturer << "\n";
370     }
371     if (info.androidSdkLevel != 0)
372     {
373         std::cout << "Android SDK Level: " << info.androidSdkLevel << "\n";
374     }
375     if (!info.machineModelName.empty())
376     {
377         std::cout << "Machine Model: " << info.machineModelName << "\n";
378     }
379     if (!info.machineModelVersion.empty())
380     {
381         std::cout << "Machine Model Version: " << info.machineModelVersion << "\n";
382     }
383     std::cout << std::endl;
384 }
385 
ParseNvidiaDriverVersion(uint32_t version)386 VersionInfo ParseNvidiaDriverVersion(uint32_t version)
387 {
388     return {
389         version >> 22,         // major
390         version >> 14 & 0xff,  // minor
391         version >> 6 & 0xff,   // subMinor
392         version & 0x3f         // patch
393     };
394 }
395 
GetSystemDeviceIdFromParts(uint32_t highPart,uint32_t lowPart)396 uint64_t GetSystemDeviceIdFromParts(uint32_t highPart, uint32_t lowPart)
397 {
398     return (static_cast<uint64_t>(highPart) << 32) | lowPart;
399 }
400 
GetSystemDeviceIdHighPart(uint64_t systemDeviceId)401 uint32_t GetSystemDeviceIdHighPart(uint64_t systemDeviceId)
402 {
403     return (systemDeviceId >> 32) & 0xffffffff;
404 }
405 
GetSystemDeviceIdLowPart(uint64_t systemDeviceId)406 uint32_t GetSystemDeviceIdLowPart(uint64_t systemDeviceId)
407 {
408     return systemDeviceId & 0xffffffff;
409 }
410 
GetPreferredDeviceString()411 std::string GetPreferredDeviceString()
412 {
413     std::string device = angle::GetEnvironmentVar(kANGLEPreferredDeviceEnv);
414     ToLower(&device);
415     return device;
416 }
417 
418 }  // namespace angle
419