blob: 0bfcff2298866ffc4f8ed5bca2c33c00dc7050b9 [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/config/gpu_info_collector.h"
// This has to be included before windows.h.
#include "third_party/re2/src/re2/re2.h"
#include <windows.h>
#include <cfgmgr32.h>
#include <d3d11.h>
#include <d3d12.h>
#include <d3d9.h>
#include <dxgi.h>
#include <setupapi.h>
#include <stddef.h>
#include <stdint.h>
#include "base/file_version_info_win.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/safe_conversions.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/trace_event/trace_event.h"
#include "base/win/scoped_com_initializer.h"
#include "third_party/vulkan/include/vulkan/vulkan.h"
namespace gpu {
namespace {
void DeviceIDToVendorAndDevice(const std::wstring& id,
uint32_t* vendor_id,
uint32_t* device_id) {
*vendor_id = 0;
*device_id = 0;
if (id.length() < 21)
return;
base::string16 vendor_id_string = id.substr(8, 4);
base::string16 device_id_string = id.substr(17, 4);
int vendor = 0;
int device = 0;
base::HexStringToInt(base::UTF16ToASCII(vendor_id_string), &vendor);
base::HexStringToInt(base::UTF16ToASCII(device_id_string), &device);
*vendor_id = vendor;
*device_id = device;
}
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// This should match enum D3DFeatureLevel in \tools\metrics\histograms\enums.xml
enum class D3D12FeatureLevel {
kD3DFeatureLevelUnknown = 0,
kD3DFeatureLevel_12_0 = 1,
kD3DFeatureLevel_12_1 = 2,
kMaxValue = kD3DFeatureLevel_12_1,
};
inline D3D12FeatureLevel ConvertToHistogramFeatureLevel(
uint32_t d3d_feature_level) {
switch (d3d_feature_level) {
case 0:
return D3D12FeatureLevel::kD3DFeatureLevelUnknown;
case D3D_FEATURE_LEVEL_12_0:
return D3D12FeatureLevel::kD3DFeatureLevel_12_0;
case D3D_FEATURE_LEVEL_12_1:
return D3D12FeatureLevel::kD3DFeatureLevel_12_1;
default:
NOTREACHED();
return D3D12FeatureLevel::kD3DFeatureLevelUnknown;
}
}
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// This should match enum VulkanVersion in \tools\metrics\histograms\enums.xml
enum class VulkanVersion {
kVulkanVersionUnknown = 0,
kVulkanVersion_1_0_0 = 1,
kVulkanVersion_1_1_0 = 2,
kMaxValue = kVulkanVersion_1_1_0,
};
inline VulkanVersion ConvertToHistogramVulkanVersion(uint32_t vulkan_version) {
switch (vulkan_version) {
case 0:
return VulkanVersion::kVulkanVersionUnknown;
case VK_MAKE_VERSION(1, 0, 0):
return VulkanVersion::kVulkanVersion_1_0_0;
case VK_MAKE_VERSION(1, 1, 0):
return VulkanVersion::kVulkanVersion_1_1_0;
default:
NOTREACHED();
return VulkanVersion::kVulkanVersionUnknown;
}
}
} // namespace
#if defined(GOOGLE_CHROME_BUILD) && defined(OFFICIAL_BUILD)
// This function has a real implementation for official builds that can
// be found in src/third_party/amd.
bool GetAMDSwitchableInfo(bool* is_switchable,
uint32_t* active_vendor_id,
uint32_t* active_device_id);
#else
bool GetAMDSwitchableInfo(bool* is_switchable,
uint32_t* active_vendor_id,
uint32_t* active_device_id) {
return false;
}
#endif
bool CollectDriverInfoD3D(const std::wstring& device_id, GPUInfo* gpu_info) {
TRACE_EVENT0("gpu", "CollectDriverInfoD3D");
// Display adapter class GUID from
// https://msdn.microsoft.com/en-us/library/windows/hardware/ff553426%28v=vs.85%29.aspx
GUID display_class = {0x4d36e968,
0xe325,
0x11ce,
{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}};
// create device info for the display device
HDEVINFO device_info =
::SetupDiGetClassDevs(&display_class, NULL, NULL, DIGCF_PRESENT);
if (device_info == INVALID_HANDLE_VALUE) {
LOG(ERROR) << "Creating device info failed";
return false;
}
std::vector<GPUInfo::GPUDevice> devices;
size_t primary_device = std::numeric_limits<size_t>::max();
bool found_amd = false;
bool found_intel = false;
bool amd_is_primary = false;
DWORD index = 0;
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) {
GPUInfo::GPUDevice device;
DWORD dwcb_data = sizeof(value);
result = RegQueryValueExW(key, L"DriverVersion", NULL, NULL,
reinterpret_cast<LPBYTE>(value), &dwcb_data);
if (result == ERROR_SUCCESS)
device.driver_version = base::UTF16ToASCII(std::wstring(value));
dwcb_data = sizeof(value);
result = RegQueryValueExW(key, L"DriverDate", NULL, NULL,
reinterpret_cast<LPBYTE>(value), &dwcb_data);
if (result == ERROR_SUCCESS)
device.driver_date = base::UTF16ToASCII(std::wstring(value));
dwcb_data = sizeof(value);
result = RegQueryValueExW(key, L"ProviderName", NULL, NULL,
reinterpret_cast<LPBYTE>(value), &dwcb_data);
if (result == ERROR_SUCCESS)
device.driver_vendor = base::UTF16ToASCII(std::wstring(value));
wchar_t new_device_id[MAX_DEVICE_ID_LEN];
CONFIGRET status = CM_Get_Device_ID(
device_info_data.DevInst, new_device_id, MAX_DEVICE_ID_LEN, 0);
if (status == CR_SUCCESS) {
std::wstring id = new_device_id;
DeviceIDToVendorAndDevice(id, &(device.vendor_id),
&(device.device_id));
if (id.compare(0, device_id.size(), device_id) == 0) {
primary_device = devices.size();
if (device.vendor_id == 0x1002)
amd_is_primary = true;
}
if (device.vendor_id == 0x8086)
found_intel = true;
if (device.vendor_id == 0x1002)
found_amd = true;
devices.push_back(device);
}
RegCloseKey(key);
}
}
}
SetupDiDestroyDeviceInfoList(device_info);
if (found_amd && found_intel) {
// Potential AMD Switchable system found.
if (!amd_is_primary) {
// Some machines aren't properly detected as AMD switchable, but count
// them anyway. This may erroneously count machines where there are
// independent AMD and Intel cards and the AMD isn't hooked up to
// anything, but that should be rare.
gpu_info->amd_switchable = true;
} else {
bool is_amd_switchable = false;
uint32_t active_vendor = 0, active_device = 0;
GetAMDSwitchableInfo(&is_amd_switchable, &active_vendor, &active_device);
gpu_info->amd_switchable = is_amd_switchable;
}
}
bool found = false;
for (size_t i = 0; i < devices.size(); ++i) {
const GPUInfo::GPUDevice& device = devices[i];
if (i == primary_device) {
found = true;
gpu_info->gpu = device;
} else {
gpu_info->secondary_gpus.push_back(device);
}
}
return found;
}
// DirectX 12 are included with Windows 10 and Server 2016.
void GetGpuSupportedD3D12Version(GPUInfo* gpu_info) {
TRACE_EVENT0("gpu", "GetGpuSupportedD3D12Version");
gpu_info->supports_dx12 = false;
gpu_info->d3d12_feature_level = 0;
base::NativeLibrary d3d12_library =
base::LoadNativeLibrary(base::FilePath(L"d3d12.dll"), nullptr);
if (!d3d12_library) {
return;
}
// The order of feature levels to attempt to create in D3D CreateDevice
const D3D_FEATURE_LEVEL feature_levels[] = {D3D_FEATURE_LEVEL_12_1,
D3D_FEATURE_LEVEL_12_0};
PFN_D3D12_CREATE_DEVICE D3D12CreateDevice =
reinterpret_cast<PFN_D3D12_CREATE_DEVICE>(
GetProcAddress(d3d12_library, "D3D12CreateDevice"));
if (D3D12CreateDevice) {
// For the default adapter only. (*pAdapter == nullptr)
// Check to see if the adapter supports Direct3D 12, but don't create the
// actual device yet. (**ppDevice == nullptr)
for (auto level : feature_levels) {
if (SUCCEEDED(D3D12CreateDevice(nullptr, level, _uuidof(ID3D12Device),
nullptr))) {
gpu_info->d3d12_feature_level = level;
gpu_info->supports_dx12 = true;
break;
}
}
}
base::UnloadNativeLibrary(d3d12_library);
}
bool BadAMDVulkanDriverVersion(GPUInfo* gpu_info) {
// Both 32-bit and 64-bit dll are broken. If 64-bit doesn't exist,
// 32-bit dll will be used to detect the AMD Vulkan driver.
const base::FilePath kAmdDriver64(FILE_PATH_LITERAL("amdvlk64.dll"));
const base::FilePath kAmdDriver32(FILE_PATH_LITERAL("amdvlk32.dll"));
auto file_version_info =
base::WrapUnique(FileVersionInfoWin::CreateFileVersionInfo(kAmdDriver64));
if (!file_version_info) {
file_version_info.reset(
FileVersionInfoWin::CreateFileVersionInfo(kAmdDriver32));
if (!file_version_info)
return false;
}
const VS_FIXEDFILEINFO* fixed_file_info =
static_cast<FileVersionInfoWin*>(file_version_info.get())
->fixed_file_info();
const int major = HIWORD(fixed_file_info->dwFileVersionMS);
const int minor = LOWORD(fixed_file_info->dwFileVersionMS);
const int minor_1 = HIWORD(fixed_file_info->dwFileVersionLS);
// From the Canary crash logs, the broken amdvlk64.dll versions
// are 1.0.39.0, 1.0.51.0 and 1.0.54.0. In the manual test, version
// 9.2.10.1 dated 12/6/2017 works and version 1.0.54.0 dated 11/2/1017
// crashes. All version numbers small than 1.0.54.0 will be marked as
// broken.
if (major == 1 && minor == 0 && minor_1 <= 54) {
return true;
}
return false;
}
bool BadVulkanDllVersion(GPUInfo* gpu_info) {
std::unique_ptr<FileVersionInfoWin> file_version_info(
static_cast<FileVersionInfoWin*>(
FileVersionInfoWin::CreateFileVersionInfo(
base::FilePath(FILE_PATH_LITERAL("vulkan-1.dll")))));
if (!file_version_info)
return false;
const VS_FIXEDFILEINFO* fixed_file_info =
static_cast<FileVersionInfoWin*>(file_version_info.get())
->fixed_file_info();
const int major = HIWORD(fixed_file_info->dwFileVersionMS);
const int minor = LOWORD(fixed_file_info->dwFileVersionMS);
const int build_1 = HIWORD(fixed_file_info->dwFileVersionLS);
const int build_2 = LOWORD(fixed_file_info->dwFileVersionLS);
// From the logs, most vulkan-1.dll crashs are from the following versions.
// As of 7/23/2018.
// 0.0.0.0 - # of crashes: 6556
// 1.0.26.0 - # of crashes: 5890
// 1.0.33.0 - # of crashes: 12271
// 1.0.42.0 - # of crashes: 35749
// 1.0.42.1 - # of crashes: 68214
// 1.0.51.0 - # of crashes: 5152
// The GPU could be from any vendor, but only some certain models would crash.
// For those that don't crash, they usually return failures upon GPU vulkan
// support querying even though the GPU drivers can support it.
if ((major == 0 && minor == 0 && build_1 == 0 && build_2 == 0) ||
(major == 1 && minor == 0 && build_1 == 26 && build_2 == 0) ||
(major == 1 && minor == 0 && build_1 == 33 && build_2 == 0) ||
(major == 1 && minor == 0 && build_1 == 42 && build_2 == 0) ||
(major == 1 && minor == 0 && build_1 == 42 && build_2 == 1) ||
(major == 1 && minor == 0 && build_1 == 51 && build_2 == 0)) {
return true;
}
return false;
}
bool InitVulkan(base::NativeLibrary* vulkan_library,
PFN_vkGetInstanceProcAddr* vkGetInstanceProcAddr,
PFN_vkCreateInstance* vkCreateInstance) {
*vulkan_library =
base::LoadNativeLibrary(base::FilePath(L"vulkan-1.dll"), nullptr);
if (!(*vulkan_library)) {
return false;
}
*vkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(
GetProcAddress(*vulkan_library, "vkGetInstanceProcAddr"));
if (*vkGetInstanceProcAddr) {
*vkCreateInstance = reinterpret_cast<PFN_vkCreateInstance>(
(*vkGetInstanceProcAddr)(nullptr, "vkCreateInstance"));
if (*vkCreateInstance) {
return true;
}
}
base::UnloadNativeLibrary(*vulkan_library);
return false;
}
bool InitVulkanInstanceProc(
const VkInstance& vk_instance,
const PFN_vkGetInstanceProcAddr& vkGetInstanceProcAddr,
PFN_vkDestroyInstance* vkDestroyInstance,
PFN_vkEnumeratePhysicalDevices* vkEnumeratePhysicalDevices,
PFN_vkEnumerateDeviceExtensionProperties*
vkEnumerateDeviceExtensionProperties) {
*vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(
vkGetInstanceProcAddr(vk_instance, "vkDestroyInstance"));
*vkEnumeratePhysicalDevices =
reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(
vkGetInstanceProcAddr(vk_instance, "vkEnumeratePhysicalDevices"));
*vkEnumerateDeviceExtensionProperties =
reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(
vkGetInstanceProcAddr(vk_instance,
"vkEnumerateDeviceExtensionProperties"));
if ((*vkDestroyInstance) && (*vkEnumeratePhysicalDevices) &&
(*vkEnumerateDeviceExtensionProperties)) {
return true;
}
return false;
}
void GetGpuSupportedVulkanVersionAndExtensions(
GPUInfo* gpu_info,
const std::vector<const char*>& requested_vulkan_extensions,
std::vector<bool>* extension_support) {
TRACE_EVENT0("gpu", "GetGpuSupportedVulkanVersionAndExtensions");
base::NativeLibrary vulkan_library;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
PFN_vkCreateInstance vkCreateInstance;
PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices;
PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties;
PFN_vkDestroyInstance vkDestroyInstance;
VkInstance vk_instance = VK_NULL_HANDLE;
uint32_t physical_device_count = 0;
gpu_info->supports_vulkan = false;
gpu_info->vulkan_version = 0;
// Skip if the system has an older AMD Vulkan driver amdvlk64.dll or
// amdvlk32.dll which crashes when vkCreateInstance() is called. This bug has
// been fixed in the latest AMD driver.
if (BadAMDVulkanDriverVersion(gpu_info)) {
return;
}
// Some early versions of vulkan-1.dll might crash
if (BadVulkanDllVersion(gpu_info)) {
return;
}
if (!InitVulkan(&vulkan_library, &vkGetInstanceProcAddr, &vkCreateInstance)) {
return;
}
VkApplicationInfo app_info = {};
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
VkInstanceCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.pApplicationInfo = &app_info;
// Get the Vulkan API version supported in the GPU driver
for (int minor_version = 1; minor_version >= 0; --minor_version) {
app_info.apiVersion = VK_MAKE_VERSION(1, minor_version, 0);
VkResult result = vkCreateInstance(&create_info, nullptr, &vk_instance);
if (result == VK_SUCCESS && vk_instance &&
InitVulkanInstanceProc(vk_instance, vkGetInstanceProcAddr,
&vkDestroyInstance, &vkEnumeratePhysicalDevices,
&vkEnumerateDeviceExtensionProperties)) {
result = vkEnumeratePhysicalDevices(vk_instance, &physical_device_count,
nullptr);
if (result == VK_SUCCESS && physical_device_count > 0) {
gpu_info->supports_vulkan = true;
gpu_info->vulkan_version = app_info.apiVersion;
break;
} else {
vkDestroyInstance(vk_instance, nullptr);
vk_instance = VK_NULL_HANDLE;
}
}
}
// Check whether the requested_vulkan_extensions are supported
if (gpu_info->supports_vulkan) {
std::vector<VkPhysicalDevice> physical_devices(physical_device_count);
vkEnumeratePhysicalDevices(vk_instance, &physical_device_count,
physical_devices.data());
// physical_devices[0]: Only query the default device for now
uint32_t property_count;
vkEnumerateDeviceExtensionProperties(physical_devices[0], nullptr,
&property_count, nullptr);
std::vector<VkExtensionProperties> extension_properties(property_count);
if (property_count > 0) {
vkEnumerateDeviceExtensionProperties(physical_devices[0], nullptr,
&property_count,
extension_properties.data());
}
for (size_t i = 0; i < requested_vulkan_extensions.size(); ++i) {
for (size_t p = 0; p < property_count; ++p) {
if (strcmp(requested_vulkan_extensions[i],
extension_properties[p].extensionName) == 0) {
(*extension_support)[i] = true;
break;
}
}
}
}
if (vk_instance) {
vkDestroyInstance(vk_instance, nullptr);
}
base::UnloadNativeLibrary(vulkan_library);
}
void RecordGpuSupportedRuntimeVersionHistograms(GPUInfo* gpu_info) {
// D3D
GetGpuSupportedD3D12Version(gpu_info);
UMA_HISTOGRAM_BOOLEAN("GPU.SupportsDX12", gpu_info->supports_dx12);
UMA_HISTOGRAM_ENUMERATION(
"GPU.D3D12FeatureLevel",
ConvertToHistogramFeatureLevel(gpu_info->d3d12_feature_level));
// Vulkan
const std::vector<const char*> vulkan_extensions = {
"VK_KHR_external_memory_win32", "VK_KHR_external_semaphore_win32",
"VK_KHR_win32_keyed_mutex"};
std::vector<bool> extension_support(vulkan_extensions.size(), false);
GetGpuSupportedVulkanVersionAndExtensions(gpu_info, vulkan_extensions,
&extension_support);
UMA_HISTOGRAM_BOOLEAN("GPU.SupportsVulkan", gpu_info->supports_vulkan);
UMA_HISTOGRAM_ENUMERATION(
"GPU.VulkanVersion",
ConvertToHistogramVulkanVersion(gpu_info->vulkan_version));
for (size_t i = 0; i < vulkan_extensions.size(); ++i) {
std::string name = "GPU.VulkanExtSupport.";
name.append(vulkan_extensions[i]);
base::UmaHistogramBoolean(name, extension_support[i]);
}
}
bool CollectContextGraphicsInfo(GPUInfo* gpu_info,
const GpuPreferences& gpu_preferences) {
TRACE_EVENT0("gpu", "CollectGraphicsInfo");
DCHECK(gpu_info);
if (!CollectGraphicsInfoGL(gpu_info, gpu_preferences))
return false;
// ANGLE's renderer strings are of the form:
// ANGLE (<adapter_identifier> Direct3D<version> vs_x_x ps_x_x)
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;
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->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);
DCHECK(!gpu_info->vertex_shader_version.empty());
// Note: do not reorder, used by UMA_HISTOGRAM below
enum ShaderModel {
SHADER_MODEL_UNKNOWN,
SHADER_MODEL_2_0,
SHADER_MODEL_3_0,
SHADER_MODEL_4_0,
SHADER_MODEL_4_1,
SHADER_MODEL_5_0,
NUM_SHADER_MODELS
};
ShaderModel shader_model = SHADER_MODEL_UNKNOWN;
if (gpu_info->vertex_shader_version == "5.0") {
shader_model = SHADER_MODEL_5_0;
} else if (gpu_info->vertex_shader_version == "4.1") {
shader_model = SHADER_MODEL_4_1;
} else if (gpu_info->vertex_shader_version == "4.0") {
shader_model = SHADER_MODEL_4_0;
} else if (gpu_info->vertex_shader_version == "3.0") {
shader_model = SHADER_MODEL_3_0;
} else if (gpu_info->vertex_shader_version == "2.0") {
shader_model = SHADER_MODEL_2_0;
}
UMA_HISTOGRAM_ENUMERATION("GPU.D3DShaderModel", shader_model,
NUM_SHADER_MODELS);
// DirectX diagnostics are collected asynchronously because it takes a
// couple of seconds.
}
return true;
}
bool CollectBasicGraphicsInfo(GPUInfo* gpu_info) {
TRACE_EVENT0("gpu", "CollectPreliminaryGraphicsInfo");
DCHECK(gpu_info);
// nvd3d9wrap.dll is loaded into all processes when Optimus is enabled.
HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll");
gpu_info->optimus = nvd3d9wrap != nullptr;
// Taken from http://www.nvidia.com/object/device_ids.html
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) {
// EnumDisplayDevices returns an empty id when called inside a remote
// session (unless that session happens to be attached to the console). In
// that case, we do not want to fail, as we should be able to grab the
// device/vendor ids from the D3D context, below. Therefore, only fail if
// the device string is not one of either the RDP mirror driver "RDPUDD
// Chained DD" or the citrix display driver.
if (wcscmp(dd.DeviceString, L"RDPUDD Chained DD") != 0 &&
wcscmp(dd.DeviceString, L"Citrix Systems Inc. Display Driver") != 0) {
// Set vendor_id/device_id for blacklisting purpose.
gpu_info->gpu.vendor_id = 0xffff;
gpu_info->gpu.device_id = 0xfffe;
return false;
}
}
DeviceIDToVendorAndDevice(id, &gpu_info->gpu.vendor_id,
&gpu_info->gpu.device_id);
// TODO(zmo): we only need to call CollectDriverInfoD3D() if we use ANGLE.
return CollectDriverInfoD3D(id, gpu_info);
}
} // namespace gpu