// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "gpu_test_config.h" #include "gpu_info.h" #include "gpu_test_expectations_parser.h" #if defined(OS_LINUX) extern "C" { # include } #endif #if defined(OS_MACOSX) #include "gpu_test_config_mac.h" #endif using namespace gpu; #if defined(OS_WIN) namespace base { namespace { // Disable the deprecated function warning for GetVersionEx #pragma warning(disable: 4996) class SysInfo { public: static void OperatingSystemVersionNumbers( int32 *major_version, int32 *minor_version, int32 *bugfix_version); }; // static void SysInfo::OperatingSystemVersionNumbers( int32 *major_version, int32 *minor_version, int32 *bugfix_version) { OSVERSIONINFOEX version_info = { sizeof version_info }; ::GetVersionEx(reinterpret_cast(&version_info)); *major_version = version_info.dwMajorVersion; *minor_version = version_info.dwMinorVersion; *bugfix_version = version_info.dwBuildNumber; } } // anonymous namespace } // namespace base void DeviceIDToVendorAndDevice(const std::string& id, uint32* vendor_id, uint32* device_id) { *vendor_id = 0; *device_id = 0; if (id.length() < 21) return; std::string vendor_id_string = id.substr(8, 4); std::string device_id_string = id.substr(17, 4); base::HexStringToUInt(vendor_id_string, vendor_id); base::HexStringToUInt(device_id_string, device_id); } CollectInfoResult CollectGpuID(uint32* vendor_id, uint32* device_id) { DCHECK(vendor_id && device_id); *vendor_id = 0; *device_id = 0; // Taken from http://developer.nvidia.com/object/device_ids.html DISPLAY_DEVICEA dd; dd.cb = sizeof(DISPLAY_DEVICEA); std::string id; for (int i = 0; EnumDisplayDevicesA(NULL, i, &dd, 0); ++i) { if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) { id = dd.DeviceID; break; } } if (id.length() > 20) { DeviceIDToVendorAndDevice(id, vendor_id, device_id); if (*vendor_id != 0 && *device_id != 0) return kCollectInfoSuccess; } return kCollectInfoNonFatalFailure; } #endif // defined(OS_WIN) #if defined(OS_LINUX) const uint32 kVendorIDIntel = 0x8086; const uint32 kVendorIDNVidia = 0x10de; const uint32 kVendorIDAMD = 0x1002; CollectInfoResult CollectPCIVideoCardInfo(GPUInfo* gpu_info) { DCHECK(gpu_info); struct pci_access* access = pci_alloc(); DCHECK(access != NULL); pci_init(access); pci_scan_bus(access); bool primary_gpu_identified = false; for (pci_dev* device = access->devices; device != NULL; device = device->next) { pci_fill_info(device, 33); bool is_gpu = false; switch (device->device_class) { case PCI_CLASS_DISPLAY_VGA: case PCI_CLASS_DISPLAY_XGA: case PCI_CLASS_DISPLAY_3D: is_gpu = true; break; case PCI_CLASS_DISPLAY_OTHER: default: break; } if (!is_gpu) continue; if (device->vendor_id == 0 || device->device_id == 0) continue; GPUInfo::GPUDevice gpu; gpu.vendor_id = device->vendor_id; gpu.device_id = device->device_id; if (!primary_gpu_identified) { primary_gpu_identified = true; gpu_info->gpu = gpu; } else { // TODO(zmo): if there are multiple GPUs, we assume the non Intel // one is primary. Revisit this logic because we actually don't know // which GPU we are using at this point. if (gpu_info->gpu.vendor_id == kVendorIDIntel && gpu.vendor_id != kVendorIDIntel) { gpu_info->secondary_gpus.push_back(gpu_info->gpu); gpu_info->gpu = gpu; } else { gpu_info->secondary_gpus.push_back(gpu); } } } // Detect Optimus or AMD Switchable GPU. if (gpu_info->secondary_gpus.size() == 1 && gpu_info->secondary_gpus[0].vendor_id == kVendorIDIntel) { if (gpu_info->gpu.vendor_id == kVendorIDNVidia) gpu_info->optimus = true; if (gpu_info->gpu.vendor_id == kVendorIDAMD) gpu_info->amd_switchable = true; } pci_cleanup(access); if (!primary_gpu_identified) return kCollectInfoNonFatalFailure; return kCollectInfoSuccess; } CollectInfoResult CollectGpuID(uint32* vendor_id, uint32* device_id) { DCHECK(vendor_id && device_id); *vendor_id = 0; *device_id = 0; GPUInfo gpu_info; CollectInfoResult result = CollectPCIVideoCardInfo(&gpu_info); if (result == kCollectInfoSuccess) { *vendor_id = gpu_info.gpu.vendor_id; *device_id = gpu_info.gpu.device_id; } return result; } #endif // defined(OS_LINUX) #if defined(OS_MACOSX) CollectInfoResult CollectGpuID(uint32* vendor_id, uint32* device_id) { DCHECK(vendor_id && device_id); GPUInfo::GPUDevice gpu = GetActiveGPU(); *vendor_id = gpu.vendor_id; *device_id = gpu.device_id; if (*vendor_id != 0 && *device_id != 0) return kCollectInfoSuccess; return kCollectInfoNonFatalFailure; } #endif #if defined(OS_ANDROID) CollectInfoResult CollectGpuID(uint32 *vendor_id, uint32 *device_id) { DCHECK(vendor_id && device_id); *vendor_id = 0; *device_id = 0; return kCollectInfoNonFatalFailure; } #endif // defined(OS_ANDROID) namespace gpu { namespace { GPUTestConfig::OS GetCurrentOS() { #if defined(OS_CHROMEOS) return GPUTestConfig::kOsChromeOS; #elif defined(OS_LINUX) || defined(OS_OPENBSD) return GPUTestConfig::kOsLinux; #elif defined(OS_WIN) int32 major_version = 0; int32 minor_version = 0; int32 bugfix_version = 0; base::SysInfo::OperatingSystemVersionNumbers( &major_version, &minor_version, &bugfix_version); if (major_version == 5) return GPUTestConfig::kOsWinXP; if (major_version == 6 && minor_version == 0) return GPUTestConfig::kOsWinVista; if (major_version == 6 && minor_version == 1) return GPUTestConfig::kOsWin7; if (major_version == 6 && (minor_version == 2 || minor_version == 3)) return GPUTestConfig::kOsWin8; if (major_version == 10) return GPUTestConfig::kOsWin10; #elif defined(OS_MACOSX) int32 major_version = 0; int32 minor_version = 0; int32 bugfix_version = 0; base::SysInfo::OperatingSystemVersionNumbers( &major_version, &minor_version, &bugfix_version); if (major_version == 10) { switch (minor_version) { case 5: return GPUTestConfig::kOsMacLeopard; case 6: return GPUTestConfig::kOsMacSnowLeopard; case 7: return GPUTestConfig::kOsMacLion; case 8: return GPUTestConfig::kOsMacMountainLion; case 9: return GPUTestConfig::kOsMacMavericks; case 10: return GPUTestConfig::kOsMacYosemite; case 11: return GPUTestConfig::kOsMacElCapitan; } } #elif defined(OS_ANDROID) return GPUTestConfig::kOsAndroid; #endif return GPUTestConfig::kOsUnknown; } } // namespace anonymous GPUTestConfig::GPUTestConfig() : validate_gpu_info_(true), os_(kOsUnknown), gpu_device_id_(0), build_type_(kBuildTypeUnknown), api_(kAPIUnknown) {} GPUTestConfig::~GPUTestConfig() { } void GPUTestConfig::set_os(int32 os) { DCHECK_EQ(0, os & ~(kOsAndroid | kOsWin | kOsMac | kOsLinux | kOsChromeOS)); os_ = os; } void GPUTestConfig::AddGPUVendor(uint32 gpu_vendor) { DCHECK_NE(0u, gpu_vendor); for (size_t i = 0; i < gpu_vendor_.size(); ++i) DCHECK_NE(gpu_vendor_[i], gpu_vendor); gpu_vendor_.push_back(gpu_vendor); } void GPUTestConfig::set_gpu_device_id(uint32 id) { gpu_device_id_ = id; } void GPUTestConfig::set_build_type(int32 build_type) { DCHECK_EQ(0, build_type & ~(kBuildTypeRelease | kBuildTypeDebug)); build_type_ = build_type; } void GPUTestConfig::set_api(int32 api) { DCHECK_EQ(0, api & ~(kAPID3D9 | kAPID3D11 | kAPIGLDesktop | kAPIGLES)); api_ = api; } bool GPUTestConfig::IsValid() const { if (!validate_gpu_info_) return true; if (gpu_device_id_ != 0 && (gpu_vendor_.size() != 1 || gpu_vendor_[0] == 0)) return false; return true; } bool GPUTestConfig::OverlapsWith(const GPUTestConfig& config) const { DCHECK(IsValid()); DCHECK(config.IsValid()); if (config.os_ != kOsUnknown && os_ != kOsUnknown && (os_ & config.os_) == 0) return false; if (config.gpu_vendor_.size() > 0 && gpu_vendor_.size() > 0) { bool shared = false; for (size_t i = 0; i < config.gpu_vendor_.size() && !shared; ++i) { for (size_t j = 0; j < gpu_vendor_.size(); ++j) { if (config.gpu_vendor_[i] == gpu_vendor_[j]) { shared = true; break; } } } if (!shared) return false; } if (config.gpu_device_id_ != 0 && gpu_device_id_ != 0 && gpu_device_id_ != config.gpu_device_id_) return false; if (config.build_type_ != kBuildTypeUnknown && build_type_ != kBuildTypeUnknown && (build_type_ & config.build_type_) == 0) return false; return true; } void GPUTestConfig::DisableGPUInfoValidation() { validate_gpu_info_ = false; } void GPUTestConfig::ClearGPUVendor() { gpu_vendor_.clear(); } GPUTestBotConfig::~GPUTestBotConfig() { } void GPUTestBotConfig::AddGPUVendor(uint32 gpu_vendor) { DCHECK_EQ(0u, GPUTestConfig::gpu_vendor().size()); GPUTestConfig::AddGPUVendor(gpu_vendor); } bool GPUTestBotConfig::SetGPUInfo(const GPUInfo& gpu_info) { DCHECK(validate_gpu_info_); if (gpu_info.gpu.device_id == 0 || gpu_info.gpu.vendor_id == 0) return false; ClearGPUVendor(); AddGPUVendor(gpu_info.gpu.vendor_id); set_gpu_device_id(gpu_info.gpu.device_id); return true; } bool GPUTestBotConfig::IsValid() const { switch (os()) { case kOsWinXP: case kOsWinVista: case kOsWin7: case kOsWin8: case kOsWin10: case kOsMacLeopard: case kOsMacSnowLeopard: case kOsMacLion: case kOsMacMountainLion: case kOsMacMavericks: case kOsMacYosemite: case kOsMacElCapitan: case kOsLinux: case kOsChromeOS: case kOsAndroid: break; default: return false; } if (validate_gpu_info_) { if (gpu_vendor().size() != 1 || gpu_vendor()[0] == 0) return false; if (gpu_device_id() == 0) return false; } switch (build_type()) { case kBuildTypeRelease: case kBuildTypeDebug: break; default: return false; } return true; } bool GPUTestBotConfig::Matches(const GPUTestConfig& config) const { DCHECK(IsValid()); DCHECK(config.IsValid()); if (config.os() != kOsUnknown && (os() & config.os()) == 0) return false; if (config.gpu_vendor().size() > 0) { bool contained = false; for (size_t i = 0; i < config.gpu_vendor().size(); ++i) { if (config.gpu_vendor()[i] == gpu_vendor()[0]) { contained = true; break; } } if (!contained) return false; } if (config.gpu_device_id() != 0 && gpu_device_id() != config.gpu_device_id()) return false; if (config.build_type() != kBuildTypeUnknown && (build_type() & config.build_type()) == 0) return false; if (config.api() != 0 && (api() & config.api()) == 0) return false; return true; } bool GPUTestBotConfig::Matches(const std::string& config_data) const { GPUTestExpectationsParser parser; GPUTestConfig config; if (!parser.ParseConfig(config_data, &config)) return false; return Matches(config); } bool GPUTestBotConfig::LoadCurrentConfig(const GPUInfo *gpu_info) { bool rt; if (gpu_info == NULL) { GPUInfo my_gpu_info; CollectInfoResult result = CollectGpuID(&my_gpu_info.gpu.vendor_id, &my_gpu_info.gpu.device_id); if (result != kCollectInfoSuccess) { LOG(ERROR) << "Fail to identify GPU\n"; DisableGPUInfoValidation(); rt = true; } else { rt = SetGPUInfo(my_gpu_info); } } else { rt = SetGPUInfo(*gpu_info); } set_os(GetCurrentOS()); if (os() == kOsUnknown) { LOG(ERROR) << "Unknown OS\n"; rt = false; } #if defined(NDEBUG) set_build_type(kBuildTypeRelease); #else set_build_type(kBuildTypeDebug); #endif return rt; } // static bool GPUTestBotConfig::CurrentConfigMatches(const std::string& config_data) { GPUTestBotConfig my_config; if (!my_config.LoadCurrentConfig(NULL)) return false; return my_config.Matches(config_data); } // static bool GPUTestBotConfig::CurrentConfigMatches( const std::vector& configs) { GPUTestBotConfig my_config; if (!my_config.LoadCurrentConfig(NULL)) return false; for (size_t i = 0 ; i < configs.size(); ++i) { if (my_config.Matches(configs[i])) return true; } return false; } } // namespace gpu