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