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