| // |
| // Copyright 2013 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // SystemInfo.cpp: implementation of the system-agnostic parts of SystemInfo.h |
| |
| #include "gpu_info_util/SystemInfo.h" |
| |
| #include <cstring> |
| #include <iostream> |
| #include <sstream> |
| |
| #include "anglebase/no_destructor.h" |
| #include "common/debug.h" |
| #include "common/string_utils.h" |
| #include "common/system_utils.h" |
| |
| namespace angle |
| { |
| namespace |
| { |
| constexpr char kANGLEPreferredDeviceEnv[] = "ANGLE_PREFERRED_DEVICE"; |
| } |
| |
| std::string VendorName(VendorID vendor) |
| { |
| switch (vendor) |
| { |
| case kVendorID_AMD: |
| return "AMD"; |
| case kVendorID_ARM: |
| return "ARM"; |
| case kVendorID_Broadcom: |
| return "Broadcom"; |
| case kVendorID_GOOGLE: |
| return "Google"; |
| case kVendorID_ImgTec: |
| return "ImgTec"; |
| case kVendorID_Intel: |
| return "Intel"; |
| case kVendorID_Kazan: |
| return "Kazan"; |
| case kVendorID_NVIDIA: |
| return "NVIDIA"; |
| case kVendorID_Qualcomm: |
| return "Qualcomm"; |
| case kVendorID_VeriSilicon: |
| return "VeriSilicon"; |
| case kVendorID_Vivante: |
| return "Vivante"; |
| case kVendorID_VMWare: |
| return "VMWare"; |
| case kVendorID_Apple: |
| return "Apple"; |
| case kVendorID_Microsoft: |
| return "Microsoft"; |
| default: |
| return "Unknown (" + std::to_string(vendor) + ")"; |
| } |
| } |
| |
| GPUDeviceInfo::GPUDeviceInfo() = default; |
| |
| GPUDeviceInfo::~GPUDeviceInfo() = default; |
| |
| GPUDeviceInfo::GPUDeviceInfo(const GPUDeviceInfo &other) = default; |
| |
| SystemInfo::SystemInfo() = default; |
| |
| SystemInfo::~SystemInfo() = default; |
| |
| SystemInfo::SystemInfo(const SystemInfo &other) = default; |
| |
| bool SystemInfo::hasNVIDIAGPU() const |
| { |
| for (const GPUDeviceInfo &gpu : gpus) |
| { |
| if (IsNVIDIA(gpu.vendorId)) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool SystemInfo::hasIntelGPU() const |
| { |
| for (const GPUDeviceInfo &gpu : gpus) |
| { |
| if (IsIntel(gpu.vendorId)) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool SystemInfo::hasAMDGPU() const |
| { |
| for (const GPUDeviceInfo &gpu : gpus) |
| { |
| if (IsAMD(gpu.vendorId)) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| std::optional<size_t> SystemInfo::getPreferredGPUIndex() const |
| { |
| std::string device = GetPreferredDeviceString(); |
| if (!device.empty()) |
| { |
| for (size_t i = 0; i < gpus.size(); ++i) |
| { |
| std::string vendor = VendorName(gpus[i].vendorId); |
| ToLower(&vendor); |
| if (vendor == device) |
| return i; |
| } |
| } |
| return std::nullopt; |
| } |
| |
| bool IsAMD(VendorID vendorId) |
| { |
| return vendorId == kVendorID_AMD; |
| } |
| |
| bool IsARM(VendorID vendorId) |
| { |
| return vendorId == kVendorID_ARM; |
| } |
| |
| bool IsBroadcom(VendorID vendorId) |
| { |
| return vendorId == kVendorID_Broadcom; |
| } |
| |
| bool IsImgTec(VendorID vendorId) |
| { |
| return vendorId == kVendorID_ImgTec; |
| } |
| |
| bool IsKazan(VendorID vendorId) |
| { |
| return vendorId == kVendorID_Kazan; |
| } |
| |
| bool IsIntel(VendorID vendorId) |
| { |
| return vendorId == kVendorID_Intel; |
| } |
| |
| bool IsNVIDIA(VendorID vendorId) |
| { |
| return vendorId == kVendorID_NVIDIA; |
| } |
| |
| bool IsQualcomm(VendorID vendorId) |
| { |
| return vendorId == kVendorID_Qualcomm; |
| } |
| |
| bool IsGoogle(VendorID vendorId) |
| { |
| return vendorId == kVendorID_GOOGLE; |
| } |
| |
| bool IsVeriSilicon(VendorID vendorId) |
| { |
| return vendorId == kVendorID_VeriSilicon; |
| } |
| |
| bool IsVMWare(VendorID vendorId) |
| { |
| return vendorId == kVendorID_VMWare; |
| } |
| |
| bool IsVivante(VendorID vendorId) |
| { |
| return vendorId == kVendorID_Vivante; |
| } |
| |
| bool IsApple(VendorID vendorId) |
| { |
| return vendorId == kVendorID_Apple; |
| } |
| |
| bool IsMicrosoft(VendorID vendorId) |
| { |
| return vendorId == kVendorID_Microsoft; |
| } |
| |
| bool ParseAMDBrahmaDriverVersion(const std::string &content, std::string *version) |
| { |
| const size_t begin = content.find_first_of("0123456789"); |
| if (begin == std::string::npos) |
| { |
| return false; |
| } |
| |
| const size_t end = content.find_first_not_of("0123456789.", begin); |
| if (end == std::string::npos) |
| { |
| *version = content.substr(begin); |
| } |
| else |
| { |
| *version = content.substr(begin, end - begin); |
| } |
| return true; |
| } |
| |
| bool ParseAMDCatalystDriverVersion(const std::string &content, std::string *version) |
| { |
| std::istringstream stream(content); |
| |
| std::string line; |
| while (std::getline(stream, line)) |
| { |
| static const char kReleaseVersion[] = "ReleaseVersion="; |
| if (line.compare(0, std::strlen(kReleaseVersion), kReleaseVersion) != 0) |
| { |
| continue; |
| } |
| |
| if (ParseAMDBrahmaDriverVersion(line, version)) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool ParseMacMachineModel(const std::string &identifier, |
| std::string *type, |
| int32_t *major, |
| int32_t *minor) |
| { |
| size_t numberLoc = identifier.find_first_of("0123456789"); |
| if (numberLoc == std::string::npos) |
| { |
| return false; |
| } |
| |
| size_t commaLoc = identifier.find(',', numberLoc); |
| if (commaLoc == std::string::npos || commaLoc >= identifier.size()) |
| { |
| return false; |
| } |
| |
| const char *numberPtr = &identifier[numberLoc]; |
| const char *commaPtr = &identifier[commaLoc + 1]; |
| char *endPtr = nullptr; |
| |
| int32_t majorTmp = static_cast<int32_t>(std::strtol(numberPtr, &endPtr, 10)); |
| if (endPtr == numberPtr) |
| { |
| return false; |
| } |
| |
| int32_t minorTmp = static_cast<int32_t>(std::strtol(commaPtr, &endPtr, 10)); |
| if (endPtr == commaPtr) |
| { |
| return false; |
| } |
| |
| *major = majorTmp; |
| *minor = minorTmp; |
| *type = identifier.substr(0, numberLoc); |
| |
| return true; |
| } |
| |
| bool CMDeviceIDToDeviceAndVendorID(const std::string &id, uint32_t *vendorId, uint32_t *deviceId) |
| { |
| unsigned int vendor = 0; |
| unsigned int device = 0; |
| |
| bool success = id.length() >= 21 && HexStringToUInt(id.substr(8, 4), &vendor) && |
| HexStringToUInt(id.substr(17, 4), &device); |
| |
| *vendorId = vendor; |
| *deviceId = device; |
| return success; |
| } |
| |
| void GetDualGPUInfo(SystemInfo *info) |
| { |
| ASSERT(!info->gpus.empty()); |
| |
| // On dual-GPU systems we assume the non-Intel GPU is the graphics one. |
| // TODO: this is incorrect and problematic. activeGPUIndex must be removed if it cannot be |
| // determined correctly. A potential solution is to create an OpenGL context and parse |
| // GL_VENDOR. Currently, our test infrastructure is relying on this information and incorrectly |
| // applies test expectations on dual-GPU systems when the Intel GPU is active. |
| // http://anglebug.com/6174. |
| int active = 0; |
| bool hasIntel = false; |
| for (size_t i = 0; i < info->gpus.size(); ++i) |
| { |
| if (IsIntel(info->gpus[i].vendorId)) |
| { |
| hasIntel = true; |
| } |
| if (IsIntel(info->gpus[active].vendorId)) |
| { |
| active = static_cast<int>(i); |
| } |
| } |
| |
| // Assume that a combination of NVIDIA or AMD with Intel means Optimus or AMD Switchable |
| info->activeGPUIndex = active; |
| info->isOptimus = hasIntel && IsNVIDIA(info->gpus[active].vendorId); |
| info->isAMDSwitchable = hasIntel && IsAMD(info->gpus[active].vendorId); |
| } |
| |
| void PrintSystemInfo(const SystemInfo &info) |
| { |
| std::cout << info.gpus.size() << " GPUs:\n"; |
| |
| for (size_t i = 0; i < info.gpus.size(); i++) |
| { |
| const auto &gpu = info.gpus[i]; |
| |
| std::cout << " " << i << " - " << VendorName(gpu.vendorId) << " device id: 0x" << std::hex |
| << std::uppercase << gpu.deviceId << std::dec << ", revision id: 0x" << std::hex |
| << std::uppercase << gpu.revisionId << std::dec << ", system device id: 0x" |
| << std::hex << std::uppercase << gpu.systemDeviceId << std::dec << "\n"; |
| if (!gpu.driverVendor.empty()) |
| { |
| std::cout << " Driver Vendor: " << gpu.driverVendor << "\n"; |
| } |
| if (!gpu.driverVersion.empty()) |
| { |
| std::cout << " Driver Version: " << gpu.driverVersion << "\n"; |
| } |
| if (!gpu.driverDate.empty()) |
| { |
| std::cout << " Driver Date: " << gpu.driverDate << "\n"; |
| } |
| if (gpu.detailedDriverVersion.major != 0 || gpu.detailedDriverVersion.minor != 0 || |
| gpu.detailedDriverVersion.subMinor != 0 || gpu.detailedDriverVersion.patch != 0) |
| { |
| std::cout << " Detailed Driver Version:\n" |
| << " major: " << gpu.detailedDriverVersion.major |
| << " minor: " << gpu.detailedDriverVersion.minor |
| << " subMinor: " << gpu.detailedDriverVersion.subMinor |
| << " patch: " << gpu.detailedDriverVersion.patch << "\n"; |
| } |
| } |
| |
| std::cout << "\n"; |
| std::cout << "Active GPU: " << info.activeGPUIndex << "\n"; |
| |
| std::cout << "\n"; |
| std::cout << "Optimus: " << (info.isOptimus ? "true" : "false") << "\n"; |
| std::cout << "AMD Switchable: " << (info.isAMDSwitchable ? "true" : "false") << "\n"; |
| std::cout << "Mac Switchable: " << (info.isMacSwitchable ? "true" : "false") << "\n"; |
| std::cout << "Needs EAGL on Mac: " << (info.needsEAGLOnMac ? "true" : "false") << "\n"; |
| |
| std::cout << "\n"; |
| if (!info.machineManufacturer.empty()) |
| { |
| std::cout << "Machine Manufacturer: " << info.machineManufacturer << "\n"; |
| } |
| if (info.androidSdkLevel != 0) |
| { |
| std::cout << "Android SDK Level: " << info.androidSdkLevel << "\n"; |
| } |
| if (!info.machineModelName.empty()) |
| { |
| std::cout << "Machine Model: " << info.machineModelName << "\n"; |
| } |
| if (!info.machineModelVersion.empty()) |
| { |
| std::cout << "Machine Model Version: " << info.machineModelVersion << "\n"; |
| } |
| std::cout << std::endl; |
| } |
| |
| VersionInfo ParseNvidiaDriverVersion(uint32_t version) |
| { |
| return { |
| version >> 22, // major |
| version >> 14 & 0xff, // minor |
| version >> 6 & 0xff, // subMinor |
| version & 0x3f // patch |
| }; |
| } |
| |
| uint64_t GetSystemDeviceIdFromParts(uint32_t highPart, uint32_t lowPart) |
| { |
| return (static_cast<uint64_t>(highPart) << 32) | lowPart; |
| } |
| |
| uint32_t GetSystemDeviceIdHighPart(uint64_t systemDeviceId) |
| { |
| return (systemDeviceId >> 32) & 0xffffffff; |
| } |
| |
| uint32_t GetSystemDeviceIdLowPart(uint64_t systemDeviceId) |
| { |
| return systemDeviceId & 0xffffffff; |
| } |
| |
| std::string GetPreferredDeviceString() |
| { |
| std::string device = angle::GetEnvironmentVar(kANGLEPreferredDeviceEnv); |
| ToLower(&device); |
| return device; |
| } |
| |
| } // namespace angle |