blob: f0bf6f06356449f90ca85fb6f0ae136c64cdc897 [file] [log] [blame]
// 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 <pci/pci.h>
}
#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<OSVERSIONINFO*>(&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<std::string>& 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