| OLD | NEW | 
|    1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |    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 |    2 // Use of this source code is governed by a BSD-style license that can be | 
|    3 // found in the LICENSE file. |    3 // found in the LICENSE file. | 
|    4  |    4  | 
|    5 #include "content/gpu/gpu_info_collector.h" |    5 #include "content/gpu/gpu_info_collector.h" | 
|    6  |    6  | 
|    7 #include <windows.h> |    7 #include <windows.h> | 
|    8 #include <d3d9.h> |    8 #include <d3d9.h> | 
|    9 #include <setupapi.h> |    9 #include <setupapi.h> | 
|   10  |   10  | 
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  150                               stats.graphics * 10, 10, 200, 50); |  150                               stats.graphics * 10, 10, 200, 50); | 
|  151   UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.WinSAT.GamingScore2", |  151   UMA_HISTOGRAM_CUSTOM_COUNTS("GPU.WinSAT.GamingScore2", | 
|  152                               stats.gaming * 10, 10, 200, 50); |  152                               stats.gaming * 10, 10, 200, 50); | 
|  153   UMA_HISTOGRAM_BOOLEAN( |  153   UMA_HISTOGRAM_BOOLEAN( | 
|  154       "GPU.WinSAT.HasResults", |  154       "GPU.WinSAT.HasResults", | 
|  155       stats.overall != 0.0 && stats.graphics != 0.0 && stats.gaming != 0.0); |  155       stats.overall != 0.0 && stats.graphics != 0.0 && stats.gaming != 0.0); | 
|  156  |  156  | 
|  157   return stats; |  157   return stats; | 
|  158 } |  158 } | 
|  159  |  159  | 
|  160 }  // namespace anonymous |  160 // Advanced Micro Devices has interesting configurations on laptops were | 
|  161  |  161 // there are two videocards that can alternatively a given process output. | 
|  162 namespace gpu_info_collector { |  162 enum AMDVideoCardType { | 
 |  163   UNKNOWN, | 
 |  164   STANDALONE, | 
 |  165   INTEGRATED, | 
 |  166   SWITCHABLE | 
 |  167 }; | 
|  163  |  168  | 
|  164 #if !defined(GOOGLE_CHROME_BUILD) |  169 #if !defined(GOOGLE_CHROME_BUILD) | 
|  165 AMDVideoCardType GetAMDVideocardType() { |  170 AMDVideoCardType GetAMDVideocardType() { | 
|  166   return UNKNOWN; |  171   return UNKNOWN; | 
|  167 } |  172 } | 
|  168 #else |  173 #else | 
|  169 // This function has a real implementation for official builds that can |  174 // This function has a real implementation for official builds that can | 
|  170 // be found in src/third_party/amd. |  175 // be found in src/third_party/amd. | 
|  171 AMDVideoCardType GetAMDVideocardType(); |  176 AMDVideoCardType GetAMDVideocardType(); | 
|  172 #endif |  177 #endif | 
|  173  |  178  | 
|  174 bool CollectGraphicsInfo(content::GPUInfo* gpu_info) { |  179 bool CollectDriverInfoD3D(const std::wstring& device_id, | 
 |  180                           content::GPUInfo* gpu_info) { | 
 |  181   TRACE_EVENT0("gpu", "CollectDriverInfoD3D"); | 
 |  182  | 
 |  183   // create device info for the display device | 
 |  184   HDEVINFO device_info = SetupDiGetClassDevsW( | 
 |  185       NULL, device_id.c_str(), NULL, | 
 |  186       DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES); | 
 |  187   if (device_info == INVALID_HANDLE_VALUE) { | 
 |  188     LOG(ERROR) << "Creating device info failed"; | 
 |  189     return false; | 
 |  190   } | 
 |  191  | 
 |  192   DWORD index = 0; | 
 |  193   bool found = false; | 
 |  194   SP_DEVINFO_DATA device_info_data; | 
 |  195   device_info_data.cbSize = sizeof(device_info_data); | 
 |  196   while (SetupDiEnumDeviceInfo(device_info, index++, &device_info_data)) { | 
 |  197     WCHAR value[255]; | 
 |  198     if (SetupDiGetDeviceRegistryPropertyW(device_info, | 
 |  199                                         &device_info_data, | 
 |  200                                         SPDRP_DRIVER, | 
 |  201                                         NULL, | 
 |  202                                         reinterpret_cast<PBYTE>(value), | 
 |  203                                         sizeof(value), | 
 |  204                                         NULL)) { | 
 |  205       HKEY key; | 
 |  206       std::wstring driver_key = L"System\\CurrentControlSet\\Control\\Class\\"; | 
 |  207       driver_key += value; | 
 |  208       LONG result = RegOpenKeyExW( | 
 |  209           HKEY_LOCAL_MACHINE, driver_key.c_str(), 0, KEY_QUERY_VALUE, &key); | 
 |  210       if (result == ERROR_SUCCESS) { | 
 |  211         DWORD dwcb_data = sizeof(value); | 
 |  212         std::string driver_version; | 
 |  213         result = RegQueryValueExW( | 
 |  214             key, L"DriverVersion", NULL, NULL, | 
 |  215             reinterpret_cast<LPBYTE>(value), &dwcb_data); | 
 |  216         if (result == ERROR_SUCCESS) | 
 |  217           driver_version = WideToASCII(std::wstring(value)); | 
 |  218  | 
 |  219         std::string driver_date; | 
 |  220         dwcb_data = sizeof(value); | 
 |  221         result = RegQueryValueExW( | 
 |  222             key, L"DriverDate", NULL, NULL, | 
 |  223             reinterpret_cast<LPBYTE>(value), &dwcb_data); | 
 |  224         if (result == ERROR_SUCCESS) | 
 |  225           driver_date = WideToASCII(std::wstring(value)); | 
 |  226  | 
 |  227         std::string driver_vendor; | 
 |  228         dwcb_data = sizeof(value); | 
 |  229         result = RegQueryValueExW( | 
 |  230             key, L"ProviderName", NULL, NULL, | 
 |  231             reinterpret_cast<LPBYTE>(value), &dwcb_data); | 
 |  232         if (result == ERROR_SUCCESS) { | 
 |  233           driver_vendor = WideToASCII(std::wstring(value)); | 
 |  234           if (driver_vendor == "Advanced Micro Devices, Inc." || | 
 |  235               driver_vendor == "ATI Technologies Inc.") { | 
 |  236             // We are conservative and assume that in the absence of a clear | 
 |  237             // signal the videocard is assumed to be switchable. Additionally, | 
 |  238             // some switchable systems with Intel GPUs aren't correctly | 
 |  239             // detected, so always count them. | 
 |  240             AMDVideoCardType amd_card_type = GetAMDVideocardType(); | 
 |  241             gpu_info->amd_switchable = (gpu_info->gpu.vendor_id == 0x8086) || | 
 |  242                                        (amd_card_type != STANDALONE); | 
 |  243           } | 
 |  244         } | 
 |  245  | 
 |  246         gpu_info->driver_vendor = driver_vendor; | 
 |  247         gpu_info->driver_version = driver_version; | 
 |  248         gpu_info->driver_date = driver_date; | 
 |  249         found = true; | 
 |  250         RegCloseKey(key); | 
 |  251         break; | 
 |  252       } | 
 |  253     } | 
 |  254   } | 
 |  255   SetupDiDestroyDeviceInfoList(device_info); | 
 |  256   return found; | 
 |  257 } | 
 |  258  | 
 |  259 }  // namespace anonymous | 
 |  260  | 
 |  261 namespace gpu_info_collector { | 
 |  262  | 
 |  263 bool CollectContextGraphicsInfo(content::GPUInfo* gpu_info) { | 
|  175   TRACE_EVENT0("gpu", "CollectGraphicsInfo"); |  264   TRACE_EVENT0("gpu", "CollectGraphicsInfo"); | 
|  176  |  265  | 
|  177   DCHECK(gpu_info); |  266   DCHECK(gpu_info); | 
|  178   *gpu_info = content::GPUInfo(); |  | 
|  179  |  | 
|  180   gpu_info->performance_stats = RetrieveGpuPerformanceStats(); |  | 
|  181  |  267  | 
|  182   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseGL)) { |  268   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseGL)) { | 
|  183     std::string requested_implementation_name = |  269     std::string requested_implementation_name = | 
|  184         CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switches::kUseGL); |  270         CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switches::kUseGL); | 
|  185     if (requested_implementation_name == "swiftshader") { |  271     if (requested_implementation_name == "swiftshader") { | 
|  186       gpu_info->software_rendering = true; |  272       gpu_info->software_rendering = true; | 
|  187       return false; |  273       return false; | 
|  188     } |  274     } | 
|  189   } |  275   } | 
|  190  |  276  | 
| (...skipping 17 matching lines...) Expand all  Loading... | 
|  208     LOG(ERROR) << "display->getDevice() failed"; |  294     LOG(ERROR) << "display->getDevice() failed"; | 
|  209     return false; |  295     return false; | 
|  210   } |  296   } | 
|  211  |  297  | 
|  212   base::win::ScopedComPtr<IDirect3D9> d3d; |  298   base::win::ScopedComPtr<IDirect3D9> d3d; | 
|  213   if (FAILED(device->GetDirect3D(d3d.Receive()))) { |  299   if (FAILED(device->GetDirect3D(d3d.Receive()))) { | 
|  214     LOG(ERROR) << "device->GetDirect3D(&d3d) failed"; |  300     LOG(ERROR) << "device->GetDirect3D(&d3d) failed"; | 
|  215     return false; |  301     return false; | 
|  216   } |  302   } | 
|  217  |  303  | 
|  218   if (!CollectGraphicsInfoD3D(d3d, gpu_info)) |  304   // Get can_lose_context | 
|  219     return false; |  305   base::win::ScopedComPtr<IDirect3D9Ex> d3dex; | 
|  220  |  306   if (SUCCEEDED(d3dex.QueryFrom(d3d))) | 
|  221   // DirectX diagnostics are collected asynchronously because it takes a |  307     gpu_info->can_lose_context = false; | 
|  222   // couple of seconds. Do not mark gpu_info as complete until that is done. |  308   else | 
|  223   return true; |  309     gpu_info->can_lose_context = true; | 
|  224 } |  | 
|  225  |  | 
|  226 bool CollectPreliminaryGraphicsInfo(content::GPUInfo* gpu_info) { |  | 
|  227   TRACE_EVENT0("gpu", "CollectPreliminaryGraphicsInfo"); |  | 
|  228  |  | 
|  229   DCHECK(gpu_info); |  | 
|  230  |  | 
|  231   bool rt = true; |  | 
|  232   if (!CollectVideoCardInfo(gpu_info)) |  | 
|  233     rt = false; |  | 
|  234  |  | 
|  235   gpu_info->performance_stats = RetrieveGpuPerformanceStatsWithHistograms(); |  | 
|  236  |  | 
|  237   return rt; |  | 
|  238 } |  | 
|  239  |  | 
|  240 bool CollectGraphicsInfoD3D(IDirect3D9* d3d, content::GPUInfo* gpu_info) { |  | 
|  241   TRACE_EVENT0("gpu", "CollectGraphicsInfoD3D"); |  | 
|  242  |  | 
|  243   DCHECK(d3d); |  | 
|  244   DCHECK(gpu_info); |  | 
|  245  |  | 
|  246   bool succeed = CollectVideoCardInfo(gpu_info); |  | 
|  247  |  310  | 
|  248   // Get version information |  311   // Get version information | 
|  249   D3DCAPS9 d3d_caps; |  312   D3DCAPS9 d3d_caps; | 
|  250   if (d3d->GetDeviceCaps(D3DADAPTER_DEFAULT, |  313   if (d3d->GetDeviceCaps(D3DADAPTER_DEFAULT, | 
|  251                          D3DDEVTYPE_HAL, |  314                          D3DDEVTYPE_HAL, | 
|  252                          &d3d_caps) == D3D_OK) { |  315                          &d3d_caps) == D3D_OK) { | 
|  253     gpu_info->pixel_shader_version = |  316     gpu_info->pixel_shader_version = | 
|  254         VersionNumberToString(d3d_caps.PixelShaderVersion); |  317         VersionNumberToString(d3d_caps.PixelShaderVersion); | 
|  255     gpu_info->vertex_shader_version = |  318     gpu_info->vertex_shader_version = | 
|  256         VersionNumberToString(d3d_caps.VertexShaderVersion); |  319         VersionNumberToString(d3d_caps.VertexShaderVersion); | 
|  257   } else { |  320   } else { | 
|  258     LOG(ERROR) << "d3d->GetDeviceCaps() failed"; |  321     LOG(ERROR) << "d3d->GetDeviceCaps() failed"; | 
|  259     succeed = false; |  322     return false; | 
|  260   } |  323   } | 
|  261  |  324  | 
|  262   // Get can_lose_context |  325   // DirectX diagnostics are collected asynchronously because it takes a | 
|  263   base::win::ScopedComPtr<IDirect3D9Ex> d3dex; |  326   // couple of seconds. Do not mark gpu_info as complete until that is done. | 
|  264   if (SUCCEEDED(d3dex.QueryFrom(d3d))) |  | 
|  265     gpu_info->can_lose_context = false; |  | 
|  266   else |  | 
|  267     gpu_info->can_lose_context = true; |  | 
|  268  |  | 
|  269   return true; |  327   return true; | 
|  270 } |  328 } | 
|  271  |  329  | 
|  272 bool CollectVideoCardInfo(content::GPUInfo* gpu_info) { |  330 bool CollectBasicGraphicsInfo(content::GPUInfo* gpu_info) { | 
|  273   TRACE_EVENT0("gpu", "CollectVideoCardInfo"); |  331   TRACE_EVENT0("gpu", "CollectPreliminaryGraphicsInfo"); | 
|  274  |  332  | 
|  275   DCHECK(gpu_info); |  333   DCHECK(gpu_info); | 
|  276  |  334  | 
 |  335   gpu_info->performance_stats = RetrieveGpuPerformanceStatsWithHistograms(); | 
 |  336  | 
|  277   // nvd3d9wrap.dll is loaded into all processes when Optimus is enabled. |  337   // nvd3d9wrap.dll is loaded into all processes when Optimus is enabled. | 
|  278   HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll"); |  338   HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll"); | 
|  279   gpu_info->optimus = nvd3d9wrap != NULL; |  339   gpu_info->optimus = nvd3d9wrap != NULL; | 
|  280  |  340  | 
|  281   // Taken from http://developer.nvidia.com/object/device_ids.html |  341   // Taken from http://developer.nvidia.com/object/device_ids.html | 
|  282   DISPLAY_DEVICE dd; |  342   DISPLAY_DEVICE dd; | 
|  283   dd.cb = sizeof(DISPLAY_DEVICE); |  343   dd.cb = sizeof(DISPLAY_DEVICE); | 
|  284   int i = 0; |  344   int i = 0; | 
|  285   std::wstring id; |  345   std::wstring id; | 
|  286   for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); ++i) { |  346   for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); ++i) { | 
| (...skipping 10 matching lines...) Expand all  Loading... | 
|  297     base::HexStringToInt(WideToASCII(vendor_id_string), &vendor_id); |  357     base::HexStringToInt(WideToASCII(vendor_id_string), &vendor_id); | 
|  298     base::HexStringToInt(WideToASCII(device_id_string), &device_id); |  358     base::HexStringToInt(WideToASCII(device_id_string), &device_id); | 
|  299     gpu_info->gpu.vendor_id = vendor_id; |  359     gpu_info->gpu.vendor_id = vendor_id; | 
|  300     gpu_info->gpu.device_id = device_id; |  360     gpu_info->gpu.device_id = device_id; | 
|  301     // TODO(zmo): we only need to call CollectDriverInfoD3D() if we use ANGLE. |  361     // TODO(zmo): we only need to call CollectDriverInfoD3D() if we use ANGLE. | 
|  302     return CollectDriverInfoD3D(id, gpu_info); |  362     return CollectDriverInfoD3D(id, gpu_info); | 
|  303   } |  363   } | 
|  304   return false; |  364   return false; | 
|  305 } |  365 } | 
|  306  |  366  | 
|  307 bool CollectDriverInfoD3D(const std::wstring& device_id, |  | 
|  308                           content::GPUInfo* gpu_info) { |  | 
|  309   TRACE_EVENT0("gpu", "CollectDriverInfoD3D"); |  | 
|  310  |  | 
|  311   // create device info for the display device |  | 
|  312   HDEVINFO device_info = SetupDiGetClassDevsW( |  | 
|  313       NULL, device_id.c_str(), NULL, |  | 
|  314       DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES); |  | 
|  315   if (device_info == INVALID_HANDLE_VALUE) { |  | 
|  316     LOG(ERROR) << "Creating device info failed"; |  | 
|  317     return false; |  | 
|  318   } |  | 
|  319  |  | 
|  320   DWORD index = 0; |  | 
|  321   bool found = false; |  | 
|  322   SP_DEVINFO_DATA device_info_data; |  | 
|  323   device_info_data.cbSize = sizeof(device_info_data); |  | 
|  324   while (SetupDiEnumDeviceInfo(device_info, index++, &device_info_data)) { |  | 
|  325     WCHAR value[255]; |  | 
|  326     if (SetupDiGetDeviceRegistryPropertyW(device_info, |  | 
|  327                                         &device_info_data, |  | 
|  328                                         SPDRP_DRIVER, |  | 
|  329                                         NULL, |  | 
|  330                                         reinterpret_cast<PBYTE>(value), |  | 
|  331                                         sizeof(value), |  | 
|  332                                         NULL)) { |  | 
|  333       HKEY key; |  | 
|  334       std::wstring driver_key = L"System\\CurrentControlSet\\Control\\Class\\"; |  | 
|  335       driver_key += value; |  | 
|  336       LONG result = RegOpenKeyExW( |  | 
|  337           HKEY_LOCAL_MACHINE, driver_key.c_str(), 0, KEY_QUERY_VALUE, &key); |  | 
|  338       if (result == ERROR_SUCCESS) { |  | 
|  339         DWORD dwcb_data = sizeof(value); |  | 
|  340         std::string driver_version; |  | 
|  341         result = RegQueryValueExW( |  | 
|  342             key, L"DriverVersion", NULL, NULL, |  | 
|  343             reinterpret_cast<LPBYTE>(value), &dwcb_data); |  | 
|  344         if (result == ERROR_SUCCESS) |  | 
|  345           driver_version = WideToASCII(std::wstring(value)); |  | 
|  346  |  | 
|  347         std::string driver_date; |  | 
|  348         dwcb_data = sizeof(value); |  | 
|  349         result = RegQueryValueExW( |  | 
|  350             key, L"DriverDate", NULL, NULL, |  | 
|  351             reinterpret_cast<LPBYTE>(value), &dwcb_data); |  | 
|  352         if (result == ERROR_SUCCESS) |  | 
|  353           driver_date = WideToASCII(std::wstring(value)); |  | 
|  354  |  | 
|  355         std::string driver_vendor; |  | 
|  356         dwcb_data = sizeof(value); |  | 
|  357         result = RegQueryValueExW( |  | 
|  358             key, L"ProviderName", NULL, NULL, |  | 
|  359             reinterpret_cast<LPBYTE>(value), &dwcb_data); |  | 
|  360         if (result == ERROR_SUCCESS) { |  | 
|  361           driver_vendor = WideToASCII(std::wstring(value)); |  | 
|  362           if (driver_vendor == "Advanced Micro Devices, Inc." || |  | 
|  363               driver_vendor == "ATI Technologies Inc.") { |  | 
|  364             // We are conservative and assume that in the absence of a clear |  | 
|  365             // signal the videocard is assumed to be switchable. Additionally, |  | 
|  366             // some switchable systems with Intel GPUs aren't correctly |  | 
|  367             // detected, so always count them. |  | 
|  368             AMDVideoCardType amd_card_type = GetAMDVideocardType(); |  | 
|  369             gpu_info->amd_switchable = (gpu_info->gpu.vendor_id == 0x8086) || |  | 
|  370                                        (amd_card_type != STANDALONE); |  | 
|  371           } |  | 
|  372         } |  | 
|  373  |  | 
|  374         gpu_info->driver_vendor = driver_vendor; |  | 
|  375         gpu_info->driver_version = driver_version; |  | 
|  376         gpu_info->driver_date = driver_date; |  | 
|  377         found = true; |  | 
|  378         RegCloseKey(key); |  | 
|  379         break; |  | 
|  380       } |  | 
|  381     } |  | 
|  382   } |  | 
|  383   SetupDiDestroyDeviceInfoList(device_info); |  | 
|  384   return found; |  | 
|  385 } |  | 
|  386  |  | 
|  387 bool CollectDriverInfoGL(content::GPUInfo* gpu_info) { |  367 bool CollectDriverInfoGL(content::GPUInfo* gpu_info) { | 
|  388   TRACE_EVENT0("gpu", "CollectDriverInfoGL"); |  368   TRACE_EVENT0("gpu", "CollectDriverInfoGL"); | 
|  389  |  369  | 
|  390   DCHECK(gpu_info); |  370   DCHECK(gpu_info); | 
|  391  |  371  | 
|  392   std::string gl_version_string = gpu_info->gl_version_string; |  372   std::string gl_version_string = gpu_info->gl_version_string; | 
|  393  |  373  | 
|  394   // TODO(zmo): We assume the driver version is in the end of GL_VERSION |  374   // TODO(zmo): We assume the driver version is in the end of GL_VERSION | 
|  395   // string.  Need to verify if it is true for majority drivers. |  375   // string.  Need to verify if it is true for majority drivers. | 
|  396  |  376  | 
|  397   size_t pos = gl_version_string.find_last_not_of("0123456789."); |  377   size_t pos = gl_version_string.find_last_not_of("0123456789."); | 
|  398   if (pos != std::string::npos && pos < gl_version_string.length() - 1) { |  378   if (pos != std::string::npos && pos < gl_version_string.length() - 1) { | 
|  399     gpu_info->driver_version = gl_version_string.substr(pos + 1); |  379     gpu_info->driver_version = gl_version_string.substr(pos + 1); | 
|  400     return true; |  380     return true; | 
|  401   } |  381   } | 
|  402   return false; |  382   return false; | 
|  403 } |  383 } | 
|  404  |  384  | 
 |  385 void MergeGPUInfo(content::GPUInfo* basic_gpu_info, | 
 |  386                   const content::GPUInfo& context_gpu_info) { | 
 |  387   DCHECK(basic_gpu_info); | 
 |  388  | 
 |  389   if (context_gpu_info.software_rendering) { | 
 |  390     basic_gpu_info->software_rendering = true; | 
 |  391     return; | 
 |  392   } | 
 |  393  | 
 |  394   if (!context_gpu_info.gl_vendor.empty()) { | 
 |  395     MergeGPUInfoGL(basic_gpu_info, context_gpu_info); | 
 |  396     return; | 
 |  397   } | 
 |  398  | 
 |  399   basic_gpu_info->pixel_shader_version = | 
 |  400       context_gpu_info.pixel_shader_version; | 
 |  401   basic_gpu_info->vertex_shader_version = | 
 |  402       context_gpu_info.vertex_shader_version; | 
 |  403  | 
 |  404   basic_gpu_info->dx_diagnostics = context_gpu_info.dx_diagnostics; | 
 |  405  | 
 |  406   basic_gpu_info->can_lose_context = context_gpu_info.can_lose_context; | 
 |  407   basic_gpu_info->sandboxed = context_gpu_info.sandboxed; | 
 |  408   basic_gpu_info->gpu_accessible = context_gpu_info.gpu_accessible; | 
 |  409   basic_gpu_info->finalized = context_gpu_info.finalized; | 
 |  410   basic_gpu_info->initialization_time = context_gpu_info.initialization_time; | 
 |  411 } | 
 |  412  | 
|  405 }  // namespace gpu_info_collector |  413 }  // namespace gpu_info_collector | 
| OLD | NEW |