[email protected] | 623c0bd | 2011-03-12 01:00:41 | [diff] [blame] | 1 | // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 623c0bd | 2011-03-12 01:00:41 | [diff] [blame] | 5 | #include "content/gpu/gpu_info_collector.h" |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 6 | |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 7 | #include <d3d9.h> |
[email protected] | 76665309 | 2011-03-01 16:45:23 | [diff] [blame] | 8 | #include <setupapi.h> |
| 9 | #include <windows.h> |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 10 | |
[email protected] | 864b136 | 2010-08-19 03:49:38 | [diff] [blame] | 11 | #include "base/file_path.h" |
[email protected] | dfb2643 | 2011-02-24 21:03:03 | [diff] [blame] | 12 | #include "base/logging.h" |
[email protected] | 3b63f8f4 | 2011-03-28 01:54:15 | [diff] [blame] | 13 | #include "base/memory/scoped_native_library.h" |
[email protected] | 7004d7eb | 2011-01-21 00:27:53 | [diff] [blame] | 14 | #include "base/string_number_conversions.h" |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 15 | #include "base/string_util.h" |
[email protected] | 5ae0b28 | 2011-03-28 19:24:49 | [diff] [blame] | 16 | #include "ui/gfx/gl/gl_implementation.h" |
[email protected] | 24edbd0 | 2011-04-14 00:11:59 | [diff] [blame^] | 17 | #include "ui/gfx/gl/gl_surface_egl.h" |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 18 | |
[email protected] | 4bce24e | 2010-09-07 20:45:01 | [diff] [blame] | 19 | // ANGLE seems to require that main.h be included before any other ANGLE header. |
| 20 | #include "libEGL/main.h" |
| 21 | #include "libEGL/Display.h" |
| 22 | |
[email protected] | 6cc8ece6 | 2011-03-14 20:05:47 | [diff] [blame] | 23 | namespace { |
| 24 | |
| 25 | // The version number stores the major and minor version in the least 16 bits; |
| 26 | // for example, 2.5 is 0x00000205. |
| 27 | // Returned string is in the format of "major.minor". |
| 28 | std::string VersionNumberToString(uint32 version_number) { |
| 29 | int hi = (version_number >> 8) & 0xff; |
| 30 | int low = version_number & 0xff; |
| 31 | return base::IntToString(hi) + "." + base::IntToString(low); |
| 32 | } |
| 33 | |
| 34 | } // namespace anonymous |
| 35 | |
[email protected] | 76665309 | 2011-03-01 16:45:23 | [diff] [blame] | 36 | // Setup API functions |
| 37 | typedef HDEVINFO (WINAPI*SetupDiGetClassDevsWFunc)( |
| 38 | CONST GUID *ClassGuid, |
| 39 | PCWSTR Enumerator, |
| 40 | HWND hwndParent, |
| 41 | DWORD Flags |
| 42 | ); |
| 43 | typedef BOOL (WINAPI*SetupDiEnumDeviceInfoFunc)( |
| 44 | HDEVINFO DeviceInfoSet, |
| 45 | DWORD MemberIndex, |
| 46 | PSP_DEVINFO_DATA DeviceInfoData |
| 47 | ); |
| 48 | typedef BOOL (WINAPI*SetupDiGetDeviceRegistryPropertyWFunc)( |
| 49 | HDEVINFO DeviceInfoSet, |
| 50 | PSP_DEVINFO_DATA DeviceInfoData, |
| 51 | DWORD Property, |
| 52 | PDWORD PropertyRegDataType, |
| 53 | PBYTE PropertyBuffer, |
| 54 | DWORD PropertyBufferSize, |
| 55 | PDWORD RequiredSize |
| 56 | ); |
| 57 | typedef BOOL (WINAPI*SetupDiDestroyDeviceInfoListFunc)( |
| 58 | HDEVINFO DeviceInfoSet |
| 59 | ); |
| 60 | |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 61 | namespace gpu_info_collector { |
| 62 | |
[email protected] | 4bce24e | 2010-09-07 20:45:01 | [diff] [blame] | 63 | bool CollectGraphicsInfo(GPUInfo* gpu_info) { |
[email protected] | 7004d7eb | 2011-01-21 00:27:53 | [diff] [blame] | 64 | DCHECK(gpu_info); |
| 65 | |
| 66 | if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2) { |
[email protected] | 6cc8ece6 | 2011-03-14 20:05:47 | [diff] [blame] | 67 | gpu_info->finalized = true; |
[email protected] | 7004d7eb | 2011-01-21 00:27:53 | [diff] [blame] | 68 | return CollectGraphicsInfoGL(gpu_info); |
| 69 | } |
| 70 | |
| 71 | // TODO(zmo): the following code only works if running on top of ANGLE. |
| 72 | // Need to handle the case when running on top of real EGL/GLES2 drivers. |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 73 | |
[email protected] | 4bce24e | 2010-09-07 20:45:01 | [diff] [blame] | 74 | egl::Display* display = static_cast<egl::Display*>( |
[email protected] | 24edbd0 | 2011-04-14 00:11:59 | [diff] [blame^] | 75 | gfx::GLSurfaceEGL::GetDisplay()); |
[email protected] | dfb2643 | 2011-02-24 21:03:03 | [diff] [blame] | 76 | if (!display) { |
| 77 | LOG(ERROR) << "gfx::BaseEGLContext::GetDisplay() failed"; |
[email protected] | b60ca99 | 2010-09-20 18:57:07 | [diff] [blame] | 78 | return false; |
[email protected] | dfb2643 | 2011-02-24 21:03:03 | [diff] [blame] | 79 | } |
[email protected] | b60ca99 | 2010-09-20 18:57:07 | [diff] [blame] | 80 | |
[email protected] | 4bce24e | 2010-09-07 20:45:01 | [diff] [blame] | 81 | IDirect3DDevice9* device = display->getDevice(); |
[email protected] | dfb2643 | 2011-02-24 21:03:03 | [diff] [blame] | 82 | if (!device) { |
| 83 | LOG(ERROR) << "display->getDevice() failed"; |
[email protected] | b60ca99 | 2010-09-20 18:57:07 | [diff] [blame] | 84 | return false; |
[email protected] | dfb2643 | 2011-02-24 21:03:03 | [diff] [blame] | 85 | } |
[email protected] | b60ca99 | 2010-09-20 18:57:07 | [diff] [blame] | 86 | |
[email protected] | 4bce24e | 2010-09-07 20:45:01 | [diff] [blame] | 87 | IDirect3D9* d3d = NULL; |
[email protected] | dfb2643 | 2011-02-24 21:03:03 | [diff] [blame] | 88 | if (FAILED(device->GetDirect3D(&d3d))) { |
| 89 | LOG(ERROR) << "device->GetDirect3D(&d3d) failed"; |
[email protected] | 4bce24e | 2010-09-07 20:45:01 | [diff] [blame] | 90 | return false; |
[email protected] | dfb2643 | 2011-02-24 21:03:03 | [diff] [blame] | 91 | } |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 92 | |
[email protected] | 41579ae | 2010-11-15 22:31:26 | [diff] [blame] | 93 | if (!CollectGraphicsInfoD3D(d3d, gpu_info)) |
| 94 | return false; |
[email protected] | 81f85b3 | 2010-10-08 18:03:21 | [diff] [blame] | 95 | |
[email protected] | 41579ae | 2010-11-15 22:31:26 | [diff] [blame] | 96 | // DirectX diagnostics are collected asynchronously because it takes a |
[email protected] | dfb2643 | 2011-02-24 21:03:03 | [diff] [blame] | 97 | // couple of seconds. Do not mark gpu_info as complete until that is done. |
[email protected] | 41579ae | 2010-11-15 22:31:26 | [diff] [blame] | 98 | return true; |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 99 | } |
| 100 | |
[email protected] | 079c4ad | 2011-02-19 00:05:57 | [diff] [blame] | 101 | bool CollectPreliminaryGraphicsInfo(GPUInfo* gpu_info) { |
| 102 | DCHECK(gpu_info); |
| 103 | |
[email protected] | 079c4ad | 2011-02-19 00:05:57 | [diff] [blame] | 104 | bool rt = true; |
| 105 | if (!CollectVideoCardInfo(gpu_info)) |
| 106 | rt = false; |
| 107 | |
| 108 | return rt; |
| 109 | } |
| 110 | |
[email protected] | 4bce24e | 2010-09-07 20:45:01 | [diff] [blame] | 111 | bool CollectGraphicsInfoD3D(IDirect3D9* d3d, GPUInfo* gpu_info) { |
| 112 | DCHECK(d3d); |
| 113 | DCHECK(gpu_info); |
| 114 | |
[email protected] | 76665309 | 2011-03-01 16:45:23 | [diff] [blame] | 115 | bool succeed = CollectVideoCardInfo(gpu_info); |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 116 | |
| 117 | // Get version information |
| 118 | D3DCAPS9 d3d_caps; |
[email protected] | 7004d7eb | 2011-01-21 00:27:53 | [diff] [blame] | 119 | if (d3d->GetDeviceCaps(D3DADAPTER_DEFAULT, |
| 120 | D3DDEVTYPE_HAL, |
| 121 | &d3d_caps) == D3D_OK) { |
[email protected] | 6cc8ece6 | 2011-03-14 20:05:47 | [diff] [blame] | 122 | gpu_info->pixel_shader_version = |
| 123 | VersionNumberToString(d3d_caps.PixelShaderVersion); |
| 124 | gpu_info->vertex_shader_version = |
| 125 | VersionNumberToString(d3d_caps.VertexShaderVersion); |
[email protected] | 7004d7eb | 2011-01-21 00:27:53 | [diff] [blame] | 126 | } else { |
[email protected] | dfb2643 | 2011-02-24 21:03:03 | [diff] [blame] | 127 | LOG(ERROR) << "d3d->GetDeviceCaps() failed"; |
[email protected] | 7004d7eb | 2011-01-21 00:27:53 | [diff] [blame] | 128 | succeed = false; |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 129 | } |
[email protected] | 4bce24e | 2010-09-07 20:45:01 | [diff] [blame] | 130 | |
[email protected] | 7004d7eb | 2011-01-21 00:27:53 | [diff] [blame] | 131 | // Get can_lose_context |
[email protected] | 4bce24e | 2010-09-07 20:45:01 | [diff] [blame] | 132 | bool can_lose_context = false; |
| 133 | IDirect3D9Ex* d3dex = NULL; |
| 134 | if (SUCCEEDED(d3d->QueryInterface(__uuidof(IDirect3D9Ex), |
| 135 | reinterpret_cast<void**>(&d3dex)))) { |
| 136 | d3dex->Release(); |
| 137 | } else { |
| 138 | can_lose_context = true; |
| 139 | } |
[email protected] | a61508e5 | 2011-03-08 17:59:42 | [diff] [blame] | 140 | gpu_info->can_lose_context = can_lose_context; |
[email protected] | 4bce24e | 2010-09-07 20:45:01 | [diff] [blame] | 141 | |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 142 | d3d->Release(); |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 143 | return true; |
| 144 | } |
| 145 | |
[email protected] | 7004d7eb | 2011-01-21 00:27:53 | [diff] [blame] | 146 | bool CollectVideoCardInfo(GPUInfo* gpu_info) { |
| 147 | DCHECK(gpu_info); |
| 148 | |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 149 | // Taken from http://developer.nvidia.com/object/device_ids.html |
| 150 | DISPLAY_DEVICE dd; |
| 151 | dd.cb = sizeof(DISPLAY_DEVICE); |
| 152 | int i = 0; |
| 153 | std::wstring id; |
| 154 | for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); ++i) { |
| 155 | if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) { |
| 156 | id = dd.DeviceID; |
| 157 | break; |
| 158 | } |
| 159 | } |
[email protected] | 7004d7eb | 2011-01-21 00:27:53 | [diff] [blame] | 160 | |
| 161 | if (id.length() > 20) { |
| 162 | int vendor_id = 0, device_id = 0; |
| 163 | std::wstring vendor_id_string = id.substr(8, 4); |
| 164 | std::wstring device_id_string = id.substr(17, 4); |
| 165 | base::HexStringToInt(WideToASCII(vendor_id_string), &vendor_id); |
| 166 | base::HexStringToInt(WideToASCII(device_id_string), &device_id); |
[email protected] | a61508e5 | 2011-03-08 17:59:42 | [diff] [blame] | 167 | gpu_info->vendor_id = vendor_id; |
| 168 | gpu_info->device_id = device_id; |
[email protected] | 93f5dcc3f | 2011-03-02 22:05:48 | [diff] [blame] | 169 | // TODO(zmo): we only need to call CollectDriverInfoD3D() if we use ANGLE. |
| 170 | return CollectDriverInfoD3D(id, gpu_info); |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 171 | } |
[email protected] | 7004d7eb | 2011-01-21 00:27:53 | [diff] [blame] | 172 | return false; |
| 173 | } |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 174 | |
[email protected] | 76665309 | 2011-03-01 16:45:23 | [diff] [blame] | 175 | bool CollectDriverInfoD3D(const std::wstring& device_id, GPUInfo* gpu_info) { |
| 176 | HMODULE lib_setupapi = LoadLibraryW(L"setupapi.dll"); |
| 177 | if (!lib_setupapi) { |
| 178 | LOG(ERROR) << "Open setupapi.dll failed"; |
| 179 | return false; |
| 180 | } |
| 181 | SetupDiGetClassDevsWFunc fp_get_class_devs = |
| 182 | reinterpret_cast<SetupDiGetClassDevsWFunc>( |
| 183 | GetProcAddress(lib_setupapi, "SetupDiGetClassDevsW")); |
| 184 | SetupDiEnumDeviceInfoFunc fp_enum_device_info = |
| 185 | reinterpret_cast<SetupDiEnumDeviceInfoFunc>( |
| 186 | GetProcAddress(lib_setupapi, "SetupDiEnumDeviceInfo")); |
| 187 | SetupDiGetDeviceRegistryPropertyWFunc fp_get_device_registry_property = |
| 188 | reinterpret_cast<SetupDiGetDeviceRegistryPropertyWFunc>( |
| 189 | GetProcAddress(lib_setupapi, "SetupDiGetDeviceRegistryPropertyW")); |
| 190 | SetupDiDestroyDeviceInfoListFunc fp_destroy_device_info_list = |
| 191 | reinterpret_cast<SetupDiDestroyDeviceInfoListFunc>( |
| 192 | GetProcAddress(lib_setupapi, "SetupDiDestroyDeviceInfoList")); |
| 193 | if (!fp_get_class_devs || !fp_enum_device_info || |
| 194 | !fp_get_device_registry_property || !fp_destroy_device_info_list) { |
| 195 | FreeLibrary(lib_setupapi); |
| 196 | LOG(ERROR) << "Retrieve setupapi.dll functions failed"; |
| 197 | return false; |
| 198 | } |
| 199 | |
| 200 | // create device info for the display device |
| 201 | HDEVINFO device_info = fp_get_class_devs( |
| 202 | NULL, device_id.c_str(), NULL, |
| 203 | DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES); |
| 204 | if (device_info == INVALID_HANDLE_VALUE) { |
| 205 | FreeLibrary(lib_setupapi); |
| 206 | LOG(ERROR) << "Creating device info failed"; |
| 207 | return false; |
| 208 | } |
| 209 | |
| 210 | DWORD index = 0; |
| 211 | bool found = false; |
| 212 | SP_DEVINFO_DATA device_info_data; |
| 213 | device_info_data.cbSize = sizeof(device_info_data); |
| 214 | while (fp_enum_device_info(device_info, index++, &device_info_data)) { |
| 215 | WCHAR value[255]; |
| 216 | if (fp_get_device_registry_property(device_info, |
| 217 | &device_info_data, |
| 218 | SPDRP_DRIVER, |
| 219 | NULL, |
| 220 | reinterpret_cast<PBYTE>(value), |
| 221 | sizeof(value), |
| 222 | NULL)) { |
| 223 | HKEY key; |
| 224 | std::wstring driver_key = L"System\\CurrentControlSet\\Control\\Class\\"; |
| 225 | driver_key += value; |
| 226 | LONG result = RegOpenKeyExW( |
| 227 | HKEY_LOCAL_MACHINE, driver_key.c_str(), 0, KEY_QUERY_VALUE, &key); |
| 228 | if (result == ERROR_SUCCESS) { |
| 229 | DWORD dwcb_data = sizeof(value); |
| 230 | std::string driver_version; |
| 231 | result = RegQueryValueExW( |
| 232 | key, L"DriverVersion", NULL, NULL, |
| 233 | reinterpret_cast<LPBYTE>(value), &dwcb_data); |
| 234 | if (result == ERROR_SUCCESS) |
| 235 | driver_version = WideToASCII(std::wstring(value)); |
| 236 | |
| 237 | std::string driver_date; |
| 238 | dwcb_data = sizeof(value); |
| 239 | result = RegQueryValueExW( |
| 240 | key, L"DriverDate", NULL, NULL, |
| 241 | reinterpret_cast<LPBYTE>(value), &dwcb_data); |
| 242 | if (result == ERROR_SUCCESS) |
| 243 | driver_date = WideToASCII(std::wstring(value)); |
| 244 | |
[email protected] | a61508e5 | 2011-03-08 17:59:42 | [diff] [blame] | 245 | gpu_info->driver_version = driver_version; |
| 246 | gpu_info->driver_date = driver_date; |
[email protected] | 76665309 | 2011-03-01 16:45:23 | [diff] [blame] | 247 | found = true; |
| 248 | RegCloseKey(key); |
| 249 | break; |
| 250 | } |
| 251 | } |
| 252 | } |
| 253 | fp_destroy_device_info_list(device_info); |
| 254 | FreeLibrary(lib_setupapi); |
| 255 | return found; |
| 256 | } |
| 257 | |
| 258 | bool CollectDriverInfoGL(GPUInfo* gpu_info) { |
[email protected] | 7004d7eb | 2011-01-21 00:27:53 | [diff] [blame] | 259 | DCHECK(gpu_info); |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 260 | |
[email protected] | a61508e5 | 2011-03-08 17:59:42 | [diff] [blame] | 261 | std::string gl_version_string = gpu_info->gl_version_string; |
[email protected] | 7004d7eb | 2011-01-21 00:27:53 | [diff] [blame] | 262 | |
| 263 | // TODO(zmo): We assume the driver version is in the end of GL_VERSION |
| 264 | // string. Need to verify if it is true for majority drivers. |
| 265 | |
| 266 | size_t pos = gl_version_string.find_last_not_of("0123456789."); |
| 267 | if (pos != std::string::npos && pos < gl_version_string.length() - 1) { |
[email protected] | a61508e5 | 2011-03-08 17:59:42 | [diff] [blame] | 268 | gpu_info->driver_version = gl_version_string.substr(pos + 1); |
[email protected] | 7004d7eb | 2011-01-21 00:27:53 | [diff] [blame] | 269 | return true; |
| 270 | } |
| 271 | return false; |
[email protected] | 1bf14bc | 2010-07-09 16:10:43 | [diff] [blame] | 272 | } |
| 273 | |
| 274 | } // namespace gpu_info_collector |