This source file includes following definitions.
- ReadXMLFloatValue
- RetrieveGpuPerformanceStats
- RetrieveGpuPerformanceStatsWithHistograms
- DisplayLinkVersion
- IsLenovoDCuteInstalled
- D3D11ShouldWork
- CollectD3D11SupportOnWorkerThread
- CollectD3D11Support
- GetAMDVideocardType
- CollectDriverInfoD3D
- CollectContextGraphicsInfo
- CollectGpuID
- CollectBasicGraphicsInfo
- CollectDriverInfoGL
- MergeGPUInfo
#include "gpu/config/gpu_info_collector.h"
#include "third_party/re2/re2/re2.h"
#include <windows.h>
#include <d3d9.h>
#include <d3d11.h>
#include <dxgi.h>
#include <setupapi.h>
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram.h"
#include "base/scoped_native_library.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "base/threading/worker_pool.h"
#include "base/win/registry.h"
#include "base/win/scoped_com_initializer.h"
#include "base/win/scoped_comptr.h"
#include "base/win/windows_version.h"
#include "third_party/libxml/chromium/libxml_utils.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_surface_egl.h"
namespace gpu {
namespace {
enum DisplayLinkInstallationStatus {
DISPLAY_LINK_NOT_INSTALLED,
DISPLAY_LINK_7_1_OR_EARLIER,
DISPLAY_LINK_7_2_OR_LATER,
DISPLAY_LINK_INSTALLATION_STATUS_MAX
};
float ReadXMLFloatValue(XmlReader* reader) {
std::string score_string;
if (!reader->ReadElementContent(&score_string))
return 0.0;
double score;
if (!base::StringToDouble(score_string, &score))
return 0.0;
return static_cast<float>(score);
}
GpuPerformanceStats RetrieveGpuPerformanceStats() {
TRACE_EVENT0("gpu", "RetrieveGpuPerformanceStats");
GpuPerformanceStats stats;
wchar_t winsat_results_path[MAX_PATH];
DWORD size = ExpandEnvironmentStrings(
L"%WinDir%\\Performance\\WinSAT\\DataStore\\",
winsat_results_path, MAX_PATH);
if (size == 0 || size > MAX_PATH) {
LOG(ERROR) << "The path to the WinSAT results is too long: "
<< size << " chars.";
return stats;
}
base::FileEnumerator file_enumerator(
base::FilePath(winsat_results_path),
false,
base::FileEnumerator::FILES,
FILE_PATH_LITERAL("* * Formal.Assessment (*).WinSAT.xml"));
base::FilePath current_results;
for (base::FilePath results = file_enumerator.Next(); !results.empty();
results = file_enumerator.Next()) {
if (base::FilePath::CompareLessIgnoreCase(current_results.value(),
results.value()))
current_results = results;
}
std::string current_results_string = current_results.MaybeAsASCII();
if (current_results_string.empty()) {
LOG(ERROR) << "Can't retrieve a valid WinSAT assessment.";
return stats;
}
XmlReader reader;
if (!reader.LoadFile(current_results_string)) {
LOG(ERROR) << "Could not open WinSAT results file.";
return stats;
}
if (!reader.SkipToElement() || !reader.Read()) {
LOG(ERROR) << "Could not read WinSAT results file.";
return stats;
}
do {
if (reader.NodeName() == "WinSPR")
break;
} while (reader.Next());
if (!reader.Read()) {
LOG(ERROR) << "Could not find WinSPR element in results file.";
return stats;
}
for (int depth = reader.Depth(); reader.Depth() == depth; reader.Next()) {
std::string node_name = reader.NodeName();
if (node_name == "SystemScore")
stats.overall = ReadXMLFloatValue(&reader);
else if (node_name == "GraphicsScore")
stats.graphics = ReadXMLFloatValue(&reader);
else if (node_name == "GamingScore")
stats.gaming = ReadXMLFloatValue(&reader);
}
if (stats.overall == 0.0)
LOG(ERROR) << "Could not read overall score from assessment results.";
if (stats.graphics == 0.0)
LOG(ERROR) << "Could not read graphics score from assessment results.";
if (stats.gaming == 0.0)
LOG(ERROR) << "Could not read gaming score from assessment results.";
return stats;
}
GpuPerformanceStats RetrieveGpuPerformanceStatsWithHistograms() {
base::TimeTicks start_time = base::TimeTicks::Now();
GpuPerformanceStats stats = RetrieveGpuPerformanceStats();
UMA_HISTOGRAM_TIMES("GPU.WinSAT.ReadResultsFileTime",
base::TimeTicks::Now() - start_time);
UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.WinSAT.OverallScore2",
stats.overall * 10, 10, 200, 50);
UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.WinSAT.GraphicsScore2",
stats.graphics * 10, 10, 200, 50);
UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.WinSAT.GamingScore2",
stats.gaming * 10, 10, 200, 50);
UMA_HISTOGRAM_BOOLEAN(
"GPU.WinSAT.HasResults",
stats.overall != 0.0 && stats.graphics != 0.0 && stats.gaming != 0.0);
return stats;
}
Version DisplayLinkVersion() {
base::win::RegKey key;
if (key.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE", KEY_READ | KEY_WOW64_64KEY))
return Version();
if (key.OpenKey(L"DisplayLink", KEY_READ | KEY_WOW64_64KEY))
return Version();
if (key.OpenKey(L"Core", KEY_READ | KEY_WOW64_64KEY))
return Version();
base::string16 version;
if (key.ReadValue(L"Version", &version))
return Version();
return Version(base::UTF16ToASCII(version));
}
bool IsLenovoDCuteInstalled() {
base::win::RegKey key;
if (key.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE", KEY_READ | KEY_WOW64_64KEY))
return false;
if (key.OpenKey(L"Lenovo", KEY_READ | KEY_WOW64_64KEY))
return false;
if (key.OpenKey(L"Lenovo dCute", KEY_READ | KEY_WOW64_64KEY))
return false;
return true;
}
bool D3D11ShouldWork(const GPUInfo& gpu_info) {
#if 1
return false;
#else
if (base::win::GetVersion() <= base::win::VERSION_VISTA)
return false;
if (gpu_info.display_link_version.IsValid())
return false;
return true;
#endif
}
void CollectD3D11SupportOnWorkerThread() {
TRACE_EVENT0("gpu", "CollectD3D11Support");
typedef HRESULT (WINAPI *D3D11CreateDeviceFunc)(
IDXGIAdapter* adapter,
D3D_DRIVER_TYPE driver_type,
HMODULE software,
UINT flags,
const D3D_FEATURE_LEVEL* feature_levels,
UINT num_feature_levels,
UINT sdk_version,
ID3D11Device** device,
D3D_FEATURE_LEVEL* feature_level,
ID3D11DeviceContext** immediate_context);
enum FeatureLevel {
FEATURE_LEVEL_UNKNOWN,
FEATURE_LEVEL_NO_D3D11_DLL,
FEATURE_LEVEL_NO_CREATE_DEVICE_ENTRY_POINT,
FEATURE_LEVEL_DEVICE_CREATION_FAILED,
FEATURE_LEVEL_9_1,
FEATURE_LEVEL_9_2,
FEATURE_LEVEL_9_3,
FEATURE_LEVEL_10_0,
FEATURE_LEVEL_10_1,
FEATURE_LEVEL_11_0,
NUM_FEATURE_LEVELS
};
FeatureLevel feature_level = FEATURE_LEVEL_UNKNOWN;
UINT bgra_support = 0;
base::NativeLibrary d3d11_module = base::LoadNativeLibrary(
base::FilePath(L"d3d11.dll"),
NULL);
if (!d3d11_module) {
feature_level = FEATURE_LEVEL_NO_D3D11_DLL;
} else {
D3D11CreateDeviceFunc create_func =
reinterpret_cast<D3D11CreateDeviceFunc>(
base::GetFunctionPointerFromNativeLibrary(d3d11_module,
"D3D11CreateDevice"));
if (!create_func) {
feature_level = FEATURE_LEVEL_NO_CREATE_DEVICE_ENTRY_POINT;
} else {
static const D3D_FEATURE_LEVEL d3d_feature_levels[] = {
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
base::win::ScopedComPtr<ID3D11Device> device;
D3D_FEATURE_LEVEL d3d_feature_level;
base::win::ScopedComPtr<ID3D11DeviceContext> device_context;
HRESULT hr = create_func(NULL,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
0,
d3d_feature_levels,
arraysize(d3d_feature_levels),
D3D11_SDK_VERSION,
device.Receive(),
&d3d_feature_level,
device_context.Receive());
if (FAILED(hr)) {
feature_level = FEATURE_LEVEL_DEVICE_CREATION_FAILED;
} else {
switch (d3d_feature_level) {
case D3D_FEATURE_LEVEL_11_0:
feature_level = FEATURE_LEVEL_11_0;
break;
case D3D_FEATURE_LEVEL_10_1:
feature_level = FEATURE_LEVEL_10_1;
break;
case D3D_FEATURE_LEVEL_10_0:
feature_level = FEATURE_LEVEL_10_0;
break;
case D3D_FEATURE_LEVEL_9_3:
feature_level = FEATURE_LEVEL_9_3;
break;
case D3D_FEATURE_LEVEL_9_2:
feature_level = FEATURE_LEVEL_9_2;
break;
case D3D_FEATURE_LEVEL_9_1:
feature_level = FEATURE_LEVEL_9_1;
break;
default:
NOTREACHED();
break;
}
hr = device->CheckFormatSupport(DXGI_FORMAT_B8G8R8A8_UNORM,
&bgra_support);
DCHECK(SUCCEEDED(hr));
}
}
}
UMA_HISTOGRAM_ENUMERATION("GPU.D3D11_FeatureLevel",
feature_level,
NUM_FEATURE_LEVELS);
if (feature_level < FEATURE_LEVEL_10_0)
return;
UMA_HISTOGRAM_BOOLEAN(
"GPU.D3D11_B8G8R8A8_Texture2DSupport",
(bgra_support & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0);
UMA_HISTOGRAM_BOOLEAN(
"GPU.D3D11_B8G8R8A8_RenderTargetSupport",
(bgra_support & D3D11_FORMAT_SUPPORT_RENDER_TARGET) != 0);
}
void CollectD3D11Support() {
base::WorkerPool::PostTask(
FROM_HERE,
base::Bind(CollectD3D11SupportOnWorkerThread),
false);
}
}
#if !defined(GOOGLE_CHROME_BUILD)
AMDVideoCardType GetAMDVideocardType() {
return STANDALONE;
}
#else
AMDVideoCardType GetAMDVideocardType();
#endif
bool CollectDriverInfoD3D(const std::wstring& device_id,
GPUInfo* gpu_info) {
TRACE_EVENT0("gpu", "CollectDriverInfoD3D");
HDEVINFO device_info = SetupDiGetClassDevsW(
NULL, device_id.c_str(), NULL,
DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES);
if (device_info == INVALID_HANDLE_VALUE) {
LOG(ERROR) << "Creating device info failed";
return false;
}
DWORD index = 0;
bool found = false;
SP_DEVINFO_DATA device_info_data;
device_info_data.cbSize = sizeof(device_info_data);
while (SetupDiEnumDeviceInfo(device_info, index++, &device_info_data)) {
WCHAR value[255];
if (SetupDiGetDeviceRegistryPropertyW(device_info,
&device_info_data,
SPDRP_DRIVER,
NULL,
reinterpret_cast<PBYTE>(value),
sizeof(value),
NULL)) {
HKEY key;
std::wstring driver_key = L"System\\CurrentControlSet\\Control\\Class\\";
driver_key += value;
LONG result = RegOpenKeyExW(
HKEY_LOCAL_MACHINE, driver_key.c_str(), 0, KEY_QUERY_VALUE, &key);
if (result == ERROR_SUCCESS) {
DWORD dwcb_data = sizeof(value);
std::string driver_version;
result = RegQueryValueExW(
key, L"DriverVersion", NULL, NULL,
reinterpret_cast<LPBYTE>(value), &dwcb_data);
if (result == ERROR_SUCCESS)
driver_version = base::UTF16ToASCII(std::wstring(value));
std::string driver_date;
dwcb_data = sizeof(value);
result = RegQueryValueExW(
key, L"DriverDate", NULL, NULL,
reinterpret_cast<LPBYTE>(value), &dwcb_data);
if (result == ERROR_SUCCESS)
driver_date = base::UTF16ToASCII(std::wstring(value));
std::string driver_vendor;
dwcb_data = sizeof(value);
result = RegQueryValueExW(
key, L"ProviderName", NULL, NULL,
reinterpret_cast<LPBYTE>(value), &dwcb_data);
if (result == ERROR_SUCCESS) {
driver_vendor = base::UTF16ToASCII(std::wstring(value));
if (driver_vendor == "Advanced Micro Devices, Inc." ||
driver_vendor == "ATI Technologies Inc.") {
AMDVideoCardType amd_card_type = GetAMDVideocardType();
gpu_info->amd_switchable = (gpu_info->gpu.vendor_id == 0x8086) ||
(amd_card_type != STANDALONE);
}
}
gpu_info->driver_vendor = driver_vendor;
gpu_info->driver_version = driver_version;
gpu_info->driver_date = driver_date;
found = true;
RegCloseKey(key);
break;
}
}
}
SetupDiDestroyDeviceInfoList(device_info);
return found;
}
CollectInfoResult CollectContextGraphicsInfo(GPUInfo* gpu_info) {
TRACE_EVENT0("gpu", "CollectGraphicsInfo");
DCHECK(gpu_info);
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseGL)) {
std::string requested_implementation_name =
CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switches::kUseGL);
if (requested_implementation_name == "swiftshader") {
gpu_info->software_rendering = true;
return kCollectInfoNonFatalFailure;
}
}
CollectInfoResult result = CollectGraphicsInfoGL(gpu_info);
if (result != kCollectInfoSuccess)
return result;
std::string direct3d_version;
int vertex_shader_major_version = 0;
int vertex_shader_minor_version = 0;
int pixel_shader_major_version = 0;
int pixel_shader_minor_version = 0;
gpu_info->adapter_luid = 0;
if (RE2::FullMatch(gpu_info->gl_renderer,
"ANGLE \\(.*\\)") &&
RE2::PartialMatch(gpu_info->gl_renderer,
" Direct3D(\\w+)",
&direct3d_version) &&
RE2::PartialMatch(gpu_info->gl_renderer,
" vs_(\\d+)_(\\d+)",
&vertex_shader_major_version,
&vertex_shader_minor_version) &&
RE2::PartialMatch(gpu_info->gl_renderer,
" ps_(\\d+)_(\\d+)",
&pixel_shader_major_version,
&pixel_shader_minor_version)) {
gpu_info->can_lose_context = direct3d_version == "9";
gpu_info->vertex_shader_version =
base::StringPrintf("%d.%d",
vertex_shader_major_version,
vertex_shader_minor_version);
gpu_info->pixel_shader_version =
base::StringPrintf("%d.%d",
pixel_shader_major_version,
pixel_shader_minor_version);
const char* egl_vendor = eglQueryString(
gfx::GLSurfaceEGL::GetHardwareDisplay(),
EGL_VENDOR);
RE2::PartialMatch(egl_vendor,
" \\(adapter LUID: ([0-9A-Fa-f]{16})\\)",
RE2::Hex(&gpu_info->adapter_luid));
gpu_info->finalized = false;
} else {
gpu_info->finalized = true;
}
return kCollectInfoSuccess;
}
GpuIDResult CollectGpuID(uint32* vendor_id, uint32* device_id) {
DCHECK(vendor_id && device_id);
*vendor_id = 0;
*device_id = 0;
DISPLAY_DEVICE dd;
dd.cb = sizeof(DISPLAY_DEVICE);
std::wstring id;
for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); ++i) {
if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
id = dd.DeviceID;
break;
}
}
if (id.length() > 20) {
int vendor = 0, device = 0;
std::wstring vendor_string = id.substr(8, 4);
std::wstring device_string = id.substr(17, 4);
base::HexStringToInt(base::UTF16ToASCII(vendor_string), &vendor);
base::HexStringToInt(base::UTF16ToASCII(device_string), &device);
*vendor_id = vendor;
*device_id = device;
return kGpuIDSuccess;
}
return kGpuIDFailure;
}
CollectInfoResult CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
TRACE_EVENT0("gpu", "CollectPreliminaryGraphicsInfo");
DCHECK(gpu_info);
gpu_info->performance_stats = RetrieveGpuPerformanceStatsWithHistograms();
HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll");
gpu_info->optimus = nvd3d9wrap != NULL;
gpu_info->lenovo_dcute = IsLenovoDCuteInstalled();
gpu_info->display_link_version = DisplayLinkVersion();
if (!gpu_info->display_link_version .IsValid()) {
UMA_HISTOGRAM_ENUMERATION("GPU.DisplayLinkInstallationStatus",
DISPLAY_LINK_NOT_INSTALLED,
DISPLAY_LINK_INSTALLATION_STATUS_MAX);
} else if (gpu_info->display_link_version.IsOlderThan("7.2")) {
UMA_HISTOGRAM_ENUMERATION("GPU.DisplayLinkInstallationStatus",
DISPLAY_LINK_7_1_OR_EARLIER,
DISPLAY_LINK_INSTALLATION_STATUS_MAX);
} else {
UMA_HISTOGRAM_ENUMERATION("GPU.DisplayLinkInstallationStatus",
DISPLAY_LINK_7_2_OR_LATER,
DISPLAY_LINK_INSTALLATION_STATUS_MAX);
}
DISPLAY_DEVICE dd;
dd.cb = sizeof(DISPLAY_DEVICE);
std::wstring id;
for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); ++i) {
if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
id = dd.DeviceID;
break;
}
}
if (id.length() <= 20)
return kCollectInfoNonFatalFailure;
int vendor_id = 0, device_id = 0;
base::string16 vendor_id_string = id.substr(8, 4);
base::string16 device_id_string = id.substr(17, 4);
base::HexStringToInt(base::UTF16ToASCII(vendor_id_string), &vendor_id);
base::HexStringToInt(base::UTF16ToASCII(device_id_string), &device_id);
gpu_info->gpu.vendor_id = vendor_id;
gpu_info->gpu.device_id = device_id;
if (!CollectDriverInfoD3D(id, gpu_info))
return kCollectInfoNonFatalFailure;
if (D3D11ShouldWork(*gpu_info)) {
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::FactoryGetFieldTrial(
"D3D11Experiment", 100, "Disabled", 2015, 7, 8,
base::FieldTrial::SESSION_RANDOMIZED, NULL));
const int enabled_group =
trial->AppendGroup("Enabled", 0);
if (trial->group() == enabled_group) {
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&CollectD3D11Support),
base::TimeDelta::FromSeconds(45));
}
}
return kCollectInfoSuccess;
}
CollectInfoResult CollectDriverInfoGL(GPUInfo* gpu_info) {
TRACE_EVENT0("gpu", "CollectDriverInfoGL");
if (!gpu_info->driver_version.empty())
return kCollectInfoSuccess;
std::string gl_version_string = gpu_info->gl_version_string;
bool parsed = RE2::PartialMatch(
gl_version_string, "([\\d\\.]+)$", &gpu_info->driver_version);
return parsed ? kCollectInfoSuccess : kCollectInfoNonFatalFailure;
}
void MergeGPUInfo(GPUInfo* basic_gpu_info,
const GPUInfo& context_gpu_info) {
DCHECK(basic_gpu_info);
if (context_gpu_info.software_rendering) {
basic_gpu_info->software_rendering = true;
return;
}
MergeGPUInfoGL(basic_gpu_info, context_gpu_info);
basic_gpu_info->dx_diagnostics = context_gpu_info.dx_diagnostics;
}
}