Fix bug in load time stats.
[chromium-blink-merge.git] / content / gpu / gpu_info_collector_win.cc
blob7891415955cbd268cda350880f7cfe81589c7c4f
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/gpu/gpu_info_collector.h"
7 #include <windows.h>
8 #include <d3d9.h>
9 #include <setupapi.h>
11 #include "base/command_line.h"
12 #include "base/debug/trace_event.h"
13 #include "base/file_path.h"
14 #include "base/file_util.h"
15 #include "base/logging.h"
16 #include "base/metrics/histogram.h"
17 #include "base/scoped_native_library.h"
18 #include "base/string_number_conversions.h"
19 #include "base/string_util.h"
20 #include "base/win/scoped_com_initializer.h"
21 #include "base/win/scoped_comptr.h"
22 #include "third_party/libxml/chromium/libxml_utils.h"
23 #include "ui/gl/gl_implementation.h"
24 #include "ui/gl/gl_surface_egl.h"
26 // ANGLE seems to require that main.h be included before any other ANGLE header.
27 #include "libEGL/Display.h"
28 #include "libEGL/main.h"
30 namespace {
32 // The version number stores the major and minor version in the least 16 bits;
33 // for example, 2.5 is 0x00000205.
34 // Returned string is in the format of "major.minor".
35 std::string VersionNumberToString(uint32 version_number) {
36 int hi = (version_number >> 8) & 0xff;
37 int low = version_number & 0xff;
38 return base::IntToString(hi) + "." + base::IntToString(low);
41 float ReadXMLFloatValue(XmlReader* reader) {
42 std::string score_string;
43 if (!reader->ReadElementContent(&score_string))
44 return 0.0;
46 double score;
47 if (!base::StringToDouble(score_string, &score))
48 return 0.0;
50 return static_cast<float>(score);
53 content::GpuPerformanceStats RetrieveGpuPerformanceStats() {
54 TRACE_EVENT0("gpu", "RetrieveGpuPerformanceStats");
56 // If the user re-runs the assessment without restarting, the COM API
57 // returns WINSAT_ASSESSMENT_STATE_NOT_AVAILABLE. Because of that and
58 // http://crbug.com/124325, read the assessment result files directly.
59 content::GpuPerformanceStats stats;
61 // Get path to WinSAT results files.
62 wchar_t winsat_results_path[MAX_PATH];
63 DWORD size = ExpandEnvironmentStrings(
64 L"%WinDir%\\Performance\\WinSAT\\DataStore\\",
65 winsat_results_path, MAX_PATH);
66 if (size == 0 || size > MAX_PATH) {
67 LOG(ERROR) << "The path to the WinSAT results is too long: "
68 << size << " chars.";
69 return stats;
72 // Find most recent formal assessment results.
73 file_util::FileEnumerator file_enumerator(
74 FilePath(winsat_results_path),
75 false, // not recursive
76 file_util::FileEnumerator::FILES,
77 FILE_PATH_LITERAL("* * Formal.Assessment (*).WinSAT.xml"));
79 FilePath current_results;
80 for (FilePath results = file_enumerator.Next(); !results.empty();
81 results = file_enumerator.Next()) {
82 // The filenames start with the date and time as yyyy-mm-dd hh.mm.ss.xxx,
83 // so the greatest file lexicographically is also the most recent file.
84 if (FilePath::CompareLessIgnoreCase(current_results.value(),
85 results.value()))
86 current_results = results;
89 std::string current_results_string = current_results.MaybeAsASCII();
90 if (current_results_string.empty()) {
91 LOG(ERROR) << "Can't retrieve a valid WinSAT assessment.";
92 return stats;
95 // Get relevant scores from results file. XML schema at:
96 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa969210.aspx
97 XmlReader reader;
98 if (!reader.LoadFile(current_results_string)) {
99 LOG(ERROR) << "Could not open WinSAT results file.";
100 return stats;
102 // Descend into <WinSAT> root element.
103 if (!reader.SkipToElement() || !reader.Read()) {
104 LOG(ERROR) << "Could not read WinSAT results file.";
105 return stats;
108 // Search for <WinSPR> element containing the results.
109 do {
110 if (reader.NodeName() == "WinSPR")
111 break;
112 } while (reader.Next());
113 // Descend into <WinSPR> element.
114 if (!reader.Read()) {
115 LOG(ERROR) << "Could not find WinSPR element in results file.";
116 return stats;
119 // Read scores.
120 for (int depth = reader.Depth(); reader.Depth() == depth; reader.Next()) {
121 std::string node_name = reader.NodeName();
122 if (node_name == "SystemScore")
123 stats.overall = ReadXMLFloatValue(&reader);
124 else if (node_name == "GraphicsScore")
125 stats.graphics = ReadXMLFloatValue(&reader);
126 else if (node_name == "GamingScore")
127 stats.gaming = ReadXMLFloatValue(&reader);
130 if (stats.overall == 0.0)
131 LOG(ERROR) << "Could not read overall score from assessment results.";
132 if (stats.graphics == 0.0)
133 LOG(ERROR) << "Could not read graphics score from assessment results.";
134 if (stats.gaming == 0.0)
135 LOG(ERROR) << "Could not read gaming score from assessment results.";
137 return stats;
140 content::GpuPerformanceStats RetrieveGpuPerformanceStatsWithHistograms() {
141 base::TimeTicks start_time = base::TimeTicks::Now();
143 content::GpuPerformanceStats stats = RetrieveGpuPerformanceStats();
145 UMA_HISTOGRAM_TIMES("GPU.WinSAT.ReadResultsFileTime",
146 base::TimeTicks::Now() - start_time);
147 UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.WinSAT.OverallScore2",
148 stats.overall * 10, 10, 200, 50);
149 UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.WinSAT.GraphicsScore2",
150 stats.graphics * 10, 10, 200, 50);
151 UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.WinSAT.GamingScore2",
152 stats.gaming * 10, 10, 200, 50);
153 UMA_HISTOGRAM_BOOLEAN(
154 "GPU.WinSAT.HasResults",
155 stats.overall != 0.0 && stats.graphics != 0.0 && stats.gaming != 0.0);
157 return stats;
160 } // namespace anonymous
162 namespace gpu_info_collector {
164 #if !defined(GOOGLE_CHROME_BUILD)
165 AMDVideoCardType GetAMDVideocardType() {
166 return UNKNOWN;
168 #else
169 // This function has a real implementation for official builds that can
170 // be found in src/third_party/amd.
171 AMDVideoCardType GetAMDVideocardType();
172 #endif
174 bool CollectGraphicsInfo(content::GPUInfo* gpu_info) {
175 TRACE_EVENT0("gpu", "CollectGraphicsInfo");
177 DCHECK(gpu_info);
179 gpu_info->performance_stats = RetrieveGpuPerformanceStats();
181 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseGL)) {
182 std::string requested_implementation_name =
183 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switches::kUseGL);
184 if (requested_implementation_name == "swiftshader") {
185 gpu_info->software_rendering = true;
186 return false;
190 if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2) {
191 gpu_info->finalized = true;
192 return CollectGraphicsInfoGL(gpu_info);
195 // TODO(zmo): the following code only works if running on top of ANGLE.
196 // Need to handle the case when running on top of real EGL/GLES2 drivers.
198 egl::Display* display = static_cast<egl::Display*>(
199 gfx::GLSurfaceEGL::GetHardwareDisplay());
200 if (!display) {
201 LOG(ERROR) << "gfx::BaseEGLContext::GetDisplay() failed";
202 return false;
205 IDirect3DDevice9* device = display->getDevice();
206 if (!device) {
207 LOG(ERROR) << "display->getDevice() failed";
208 return false;
211 base::win::ScopedComPtr<IDirect3D9> d3d;
212 if (FAILED(device->GetDirect3D(d3d.Receive()))) {
213 LOG(ERROR) << "device->GetDirect3D(&d3d) failed";
214 return false;
217 if (!CollectGraphicsInfoD3D(d3d, gpu_info))
218 return false;
220 // DirectX diagnostics are collected asynchronously because it takes a
221 // couple of seconds. Do not mark gpu_info as complete until that is done.
222 return true;
225 bool CollectPreliminaryGraphicsInfo(content::GPUInfo* gpu_info) {
226 TRACE_EVENT0("gpu", "CollectPreliminaryGraphicsInfo");
228 DCHECK(gpu_info);
230 bool rt = true;
231 if (!CollectVideoCardInfo(gpu_info))
232 rt = false;
234 gpu_info->performance_stats = RetrieveGpuPerformanceStatsWithHistograms();
236 return rt;
239 bool CollectGraphicsInfoD3D(IDirect3D9* d3d, content::GPUInfo* gpu_info) {
240 TRACE_EVENT0("gpu", "CollectGraphicsInfoD3D");
242 DCHECK(d3d);
243 DCHECK(gpu_info);
245 bool succeed = CollectVideoCardInfo(gpu_info);
247 // Get version information
248 D3DCAPS9 d3d_caps;
249 if (d3d->GetDeviceCaps(D3DADAPTER_DEFAULT,
250 D3DDEVTYPE_HAL,
251 &d3d_caps) == D3D_OK) {
252 gpu_info->pixel_shader_version =
253 VersionNumberToString(d3d_caps.PixelShaderVersion);
254 gpu_info->vertex_shader_version =
255 VersionNumberToString(d3d_caps.VertexShaderVersion);
256 } else {
257 LOG(ERROR) << "d3d->GetDeviceCaps() failed";
258 succeed = false;
261 // Get can_lose_context
262 base::win::ScopedComPtr<IDirect3D9Ex> d3dex;
263 if (SUCCEEDED(d3dex.QueryFrom(d3d)))
264 gpu_info->can_lose_context = false;
265 else
266 gpu_info->can_lose_context = true;
268 return true;
271 bool CollectVideoCardInfo(content::GPUInfo* gpu_info) {
272 TRACE_EVENT0("gpu", "CollectVideoCardInfo");
274 DCHECK(gpu_info);
276 // nvd3d9wrap.dll is loaded into all processes when Optimus is enabled.
277 HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll");
278 gpu_info->optimus = nvd3d9wrap != NULL;
280 // Taken from http://developer.nvidia.com/object/device_ids.html
281 DISPLAY_DEVICE dd;
282 dd.cb = sizeof(DISPLAY_DEVICE);
283 int i = 0;
284 std::wstring id;
285 for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); ++i) {
286 if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
287 id = dd.DeviceID;
288 break;
292 if (id.length() > 20) {
293 int vendor_id = 0, device_id = 0;
294 std::wstring vendor_id_string = id.substr(8, 4);
295 std::wstring device_id_string = id.substr(17, 4);
296 base::HexStringToInt(WideToASCII(vendor_id_string), &vendor_id);
297 base::HexStringToInt(WideToASCII(device_id_string), &device_id);
298 gpu_info->gpu.vendor_id = vendor_id;
299 gpu_info->gpu.device_id = device_id;
300 // TODO(zmo): we only need to call CollectDriverInfoD3D() if we use ANGLE.
301 return CollectDriverInfoD3D(id, gpu_info);
303 return false;
306 bool CollectDriverInfoD3D(const std::wstring& device_id,
307 content::GPUInfo* gpu_info) {
308 TRACE_EVENT0("gpu", "CollectDriverInfoD3D");
310 // create device info for the display device
311 HDEVINFO device_info = SetupDiGetClassDevsW(
312 NULL, device_id.c_str(), NULL,
313 DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES);
314 if (device_info == INVALID_HANDLE_VALUE) {
315 LOG(ERROR) << "Creating device info failed";
316 return false;
319 DWORD index = 0;
320 bool found = false;
321 SP_DEVINFO_DATA device_info_data;
322 device_info_data.cbSize = sizeof(device_info_data);
323 while (SetupDiEnumDeviceInfo(device_info, index++, &device_info_data)) {
324 WCHAR value[255];
325 if (SetupDiGetDeviceRegistryPropertyW(device_info,
326 &device_info_data,
327 SPDRP_DRIVER,
328 NULL,
329 reinterpret_cast<PBYTE>(value),
330 sizeof(value),
331 NULL)) {
332 HKEY key;
333 std::wstring driver_key = L"System\\CurrentControlSet\\Control\\Class\\";
334 driver_key += value;
335 LONG result = RegOpenKeyExW(
336 HKEY_LOCAL_MACHINE, driver_key.c_str(), 0, KEY_QUERY_VALUE, &key);
337 if (result == ERROR_SUCCESS) {
338 DWORD dwcb_data = sizeof(value);
339 std::string driver_version;
340 result = RegQueryValueExW(
341 key, L"DriverVersion", NULL, NULL,
342 reinterpret_cast<LPBYTE>(value), &dwcb_data);
343 if (result == ERROR_SUCCESS)
344 driver_version = WideToASCII(std::wstring(value));
346 std::string driver_date;
347 dwcb_data = sizeof(value);
348 result = RegQueryValueExW(
349 key, L"DriverDate", NULL, NULL,
350 reinterpret_cast<LPBYTE>(value), &dwcb_data);
351 if (result == ERROR_SUCCESS)
352 driver_date = WideToASCII(std::wstring(value));
354 std::string driver_vendor;
355 dwcb_data = sizeof(value);
356 result = RegQueryValueExW(
357 key, L"ProviderName", NULL, NULL,
358 reinterpret_cast<LPBYTE>(value), &dwcb_data);
359 if (result == ERROR_SUCCESS) {
360 driver_vendor = WideToASCII(std::wstring(value));
361 if (driver_vendor == "Advanced Micro Devices, Inc." ||
362 driver_vendor == "ATI Technologies Inc.") {
363 // We are conservative and assume that in the absense of a clear
364 // signal the videocard is assumed to be switchable.
365 AMDVideoCardType amd_card_type = GetAMDVideocardType();
366 gpu_info->amd_switchable = (amd_card_type != STANDALONE);
370 gpu_info->driver_vendor = driver_vendor;
371 gpu_info->driver_version = driver_version;
372 gpu_info->driver_date = driver_date;
373 found = true;
374 RegCloseKey(key);
375 break;
379 SetupDiDestroyDeviceInfoList(device_info);
380 return found;
383 bool CollectDriverInfoGL(content::GPUInfo* gpu_info) {
384 TRACE_EVENT0("gpu", "CollectDriverInfoGL");
386 DCHECK(gpu_info);
388 std::string gl_version_string = gpu_info->gl_version_string;
390 // TODO(zmo): We assume the driver version is in the end of GL_VERSION
391 // string. Need to verify if it is true for majority drivers.
393 size_t pos = gl_version_string.find_last_not_of("0123456789.");
394 if (pos != std::string::npos && pos < gl_version_string.length() - 1) {
395 gpu_info->driver_version = gl_version_string.substr(pos + 1);
396 return true;
398 return false;
401 } // namespace gpu_info_collector