blob: 4a24a9fbe0006d9981a42e92531cbdc28ed5d8c6 [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"
// C system before C++ system.
#include <stddef.h>
#include <stdint.h>
// This has to be included before windows.h.
#include "third_party/re2/src/re2/re2.h"
#include <windows.h>
#include <d3d11.h>
#include <d3d11_3.h>
#include <d3d12.h>
#include <dxgi.h>
#include <vulkan/vulkan.h>
#include <wrl/client.h>
#include "base/file_version_info_win.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.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/stringprintf.h"
#include "base/trace_event/trace_event.h"
#include "base/win/scoped_com_initializer.h"
#include "base/win/windows_version.h"
#include "build/branding_buildflags.h"
#include "gpu/config/gpu_util.h"
#include "ui/gl/direct_composition_surface_win.h"
#include "ui/gl/gl_angle_util_win.h"
#include "ui/gl/gl_surface_egl.h"
namespace gpu {
namespace {
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// This should match enum D3D12FeatureLevel in
// \tools\metrics\histograms\enums.xml
enum class D3D12FeatureLevel {
kD3DFeatureLevelUnknown = 0,
kD3DFeatureLevel_12_0 = 1,
kD3DFeatureLevel_12_1 = 2,
kD3DFeatureLevel_11_0 = 3,
kD3DFeatureLevel_11_1 = 4,
kD3DFeatureLevel_12_2 = 5,
kMaxValue = kD3DFeatureLevel_12_2,
};
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;
case D3D_FEATURE_LEVEL_12_2:
return D3D12FeatureLevel::kD3DFeatureLevel_12_2;
case D3D_FEATURE_LEVEL_11_0:
return D3D12FeatureLevel::kD3DFeatureLevel_11_0;
case D3D_FEATURE_LEVEL_11_1:
return D3D12FeatureLevel::kD3DFeatureLevel_11_1;
default:
NOTREACHED();
return D3D12FeatureLevel::kD3DFeatureLevelUnknown;
}
}
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class D3D12ShaderModel {
kUnknownOrNoD3D12Devices = 0,
kD3DShaderModel_5_1 = 1,
kD3DShaderModel_6_0 = 2,
kD3DShaderModel_6_1 = 3,
kD3DShaderModel_6_2 = 4,
kD3DShaderModel_6_3 = 5,
kD3DShaderModel_6_4 = 6,
kD3DShaderModel_6_5 = 7,
kD3DShaderModel_6_6 = 8,
kMaxValue = kD3DShaderModel_6_6,
};
D3D12ShaderModel ConvertToHistogramShaderVersion(uint32_t version) {
switch (version) {
case 0:
return D3D12ShaderModel::kUnknownOrNoD3D12Devices;
case D3D_SHADER_MODEL_5_1:
return D3D12ShaderModel::kD3DShaderModel_5_1;
case D3D_SHADER_MODEL_6_0:
return D3D12ShaderModel::kD3DShaderModel_6_0;
case D3D_SHADER_MODEL_6_1:
return D3D12ShaderModel::kD3DShaderModel_6_1;
case D3D_SHADER_MODEL_6_2:
return D3D12ShaderModel::kD3DShaderModel_6_2;
case D3D_SHADER_MODEL_6_3:
return D3D12ShaderModel::kD3DShaderModel_6_3;
case D3D_SHADER_MODEL_6_4:
return D3D12ShaderModel::kD3DShaderModel_6_4;
case D3D_SHADER_MODEL_6_5:
return D3D12ShaderModel::kD3DShaderModel_6_5;
case D3D_SHADER_MODEL_6_6:
return D3D12ShaderModel::kD3DShaderModel_6_6;
default:
NOTREACHED();
return D3D12ShaderModel::kUnknownOrNoD3D12Devices;
}
}
OverlaySupport FlagsToOverlaySupport(bool overlays_supported, UINT flags) {
if (flags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING)
return OverlaySupport::kScaling;
if (flags & DXGI_OVERLAY_SUPPORT_FLAG_DIRECT)
return OverlaySupport::kDirect;
if (overlays_supported)
return OverlaySupport::kSoftware;
return OverlaySupport::kNone;
}
bool GetActiveAdapterLuid(LUID* luid) {
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
gl::QueryD3D11DeviceObjectFromANGLE();
if (!d3d11_device)
return false;
Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
if (FAILED(d3d11_device.As(&dxgi_device)))
return false;
Microsoft::WRL::ComPtr<IDXGIAdapter> adapter;
if (FAILED(dxgi_device->GetAdapter(&adapter)))
return false;
DXGI_ADAPTER_DESC desc;
if (FAILED(adapter->GetDesc(&desc)))
return false;
// Zero isn't a valid LUID.
if (desc.AdapterLuid.HighPart == 0 && desc.AdapterLuid.LowPart == 0)
return false;
*luid = desc.AdapterLuid;
return true;
}
} // namespace
#if BUILDFLAG(GOOGLE_CHROME_BRANDING) && 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
// This has to be called after a context is created, active GPU is identified,
// and GPU driver bug workarounds are computed again. Otherwise the workaround
// |disable_direct_composition| may not be correctly applied.
// Also, this has to be called after falling back to SwiftShader decision is
// finalized because this function depends on GL is ANGLE's GLES or not.
void CollectHardwareOverlayInfo(OverlayInfo* overlay_info) {
if (gl::GetGLImplementation() == gl::kGLImplementationEGLANGLE) {
overlay_info->direct_composition =
gl::DirectCompositionSurfaceWin::IsDirectCompositionSupported();
overlay_info->supports_overlays =
gl::DirectCompositionSurfaceWin::AreOverlaysSupported();
overlay_info->nv12_overlay_support = FlagsToOverlaySupport(
overlay_info->supports_overlays,
gl::DirectCompositionSurfaceWin::GetOverlaySupportFlags(
DXGI_FORMAT_NV12));
overlay_info->yuy2_overlay_support = FlagsToOverlaySupport(
overlay_info->supports_overlays,
gl::DirectCompositionSurfaceWin::GetOverlaySupportFlags(
DXGI_FORMAT_YUY2));
overlay_info->bgra8_overlay_support = FlagsToOverlaySupport(
overlay_info->supports_overlays,
gl::DirectCompositionSurfaceWin::GetOverlaySupportFlags(
DXGI_FORMAT_B8G8R8A8_UNORM));
overlay_info->rgb10a2_overlay_support = FlagsToOverlaySupport(
overlay_info->supports_overlays,
gl::DirectCompositionSurfaceWin::GetOverlaySupportFlags(
DXGI_FORMAT_R10G10B10A2_UNORM));
}
}
bool CollectDriverInfoD3D(GPUInfo* gpu_info) {
TRACE_EVENT0("gpu", "CollectDriverInfoD3D");
Microsoft::WRL::ComPtr<IDXGIFactory1> dxgi_factory;
HRESULT hr = ::CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_factory));
if (FAILED(hr))
return false;
bool found_amd = false;
bool found_intel = false;
bool found_nvidia = false;
UINT i;
Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
for (i = 0; SUCCEEDED(dxgi_factory->EnumAdapters(i, &dxgi_adapter)); i++) {
DXGI_ADAPTER_DESC desc;
dxgi_adapter->GetDesc(&desc);
GPUInfo::GPUDevice device;
device.vendor_id = desc.VendorId;
device.device_id = desc.DeviceId;
device.sub_sys_id = desc.SubSysId;
device.revision = desc.Revision;
device.luid =
CHROME_LUID{desc.AdapterLuid.LowPart, desc.AdapterLuid.HighPart};
LARGE_INTEGER umd_version;
hr = dxgi_adapter->CheckInterfaceSupport(__uuidof(IDXGIDevice),
&umd_version);
if (SUCCEEDED(hr)) {
device.driver_version = base::StringPrintf(
"%d.%d.%d.%d", HIWORD(umd_version.HighPart),
LOWORD(umd_version.HighPart), HIWORD(umd_version.LowPart),
LOWORD(umd_version.LowPart));
} else {
DLOG(ERROR) << "Unable to retrieve the umd version of adapter: "
<< desc.Description << " HR: " << std::hex << hr;
}
switch (device.vendor_id) {
case 0x8086:
found_intel = true;
break;
case 0x1002:
found_amd = true;
break;
case 0x10de:
found_nvidia = true;
break;
default:
break;
}
if (i == 0) {
gpu_info->gpu = device;
} else {
gpu_info->secondary_gpus.push_back(device);
}
}
if (found_intel && base::win::GetVersion() < base::win::Version::WIN10) {
// Since Windows 10 (and Windows 8.1 on some systems), switchable graphics
// platforms are managed by Windows and each adapter is accessible as
// separate devices.
// See https://msdn.microsoft.com/en-us/windows/dn265501(v=vs.80)
if (found_amd) {
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;
} else if (found_nvidia) {
// nvd3d9wrap.dll is loaded into all processes when Optimus is enabled.
HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll");
gpu_info->optimus = nvd3d9wrap != nullptr;
}
}
return i > 0;
}
// CanCreateD3D12Device returns true/false depending on whether D3D12 device
// creation should be attempted on the passed in adapter. Returns false if there
// are known driver bugs.
bool CanCreateD3D12Device(IDXGIAdapter* dxgi_adapter) {
DXGI_ADAPTER_DESC desc;
HRESULT hr = dxgi_adapter->GetDesc(&desc);
if (FAILED(hr)) {
return false;
}
// Known driver bugs are Intel-only. Expand in the future, as necessary, for
// other IHVs.
if (desc.VendorId != 0x8086)
return true;
LARGE_INTEGER umd_version;
hr = dxgi_adapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), &umd_version);
if (FAILED(hr)) {
return false;
}
// On certain Intel drivers, the driver will crash if you call
// D3D12CreateDevice and the command line of the process is greater than 1024
// bytes. 100.9416 is the first driver to introduce the bug, while 100.9664 is
// the first driver to fix it.
if (HIWORD(umd_version.LowPart) == 100 &&
LOWORD(umd_version.LowPart) >= 9416 &&
LOWORD(umd_version.LowPart) < 9664) {
const char* command_line = GetCommandLineA();
const size_t command_line_length = strlen(command_line);
// Check for 1023 since strlen doesn't include the null terminator.
if (command_line_length > 1023) {
return false;
}
}
return true;
}
// DirectX 12 are included with Windows 10 and Server 2016.
void GetGpuSupportedD3D12Version(uint32_t& d3d12_feature_level,
uint32_t& highest_shader_model_version) {
TRACE_EVENT0("gpu", "GetGpuSupportedD3D12Version");
// Initialize to 0 to indicated an unknown type in UMA.
d3d12_feature_level = 0;
highest_shader_model_version = 0;
base::ScopedNativeLibrary d3d12_library(
base::FilePath(FILE_PATH_LITERAL("d3d12.dll")));
if (!d3d12_library.is_valid())
return;
// The order of feature levels to attempt to create in D3D CreateDevice
const D3D_FEATURE_LEVEL feature_levels[] = {
D3D_FEATURE_LEVEL_12_2, D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0,
D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0};
PFN_D3D12_CREATE_DEVICE D3D12CreateDevice =
reinterpret_cast<PFN_D3D12_CREATE_DEVICE>(
d3d12_library.GetFunctionPointer("D3D12CreateDevice"));
Microsoft::WRL::ComPtr<ID3D12Device> d3d12_device;
if (D3D12CreateDevice) {
Microsoft::WRL::ComPtr<IDXGIFactory1> dxgi_factory;
HRESULT hr = ::CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_factory));
if (FAILED(hr)) {
return;
}
Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
hr = dxgi_factory->EnumAdapters(0, &dxgi_adapter);
if (FAILED(hr)) {
return;
}
if (!CanCreateD3D12Device(dxgi_adapter.Get())) {
return;
}
// For the default adapter only: EnumAdapters(0, ...).
// Check to see if the adapter supports Direct3D 12.
for (auto level : feature_levels) {
if (SUCCEEDED(D3D12CreateDevice(dxgi_adapter.Get(), level,
_uuidof(ID3D12Device), &d3d12_device))) {
d3d12_feature_level = level;
break;
}
}
}
// Query the maximum supported shader model version.
if (d3d12_device) {
D3D12_FEATURE_DATA_SHADER_MODEL shader_model_data = {};
shader_model_data.HighestShaderModel = D3D_SHADER_MODEL_6_6;
if (SUCCEEDED(d3d12_device->CheckFeatureSupport(
D3D12_FEATURE_SHADER_MODEL, &shader_model_data,
sizeof(shader_model_data)))) {
highest_shader_model_version = shader_model_data.HighestShaderModel;
}
}
}
// The old graphics drivers are installed to the Windows system directory
// c:\windows\system32 or SysWOW64. Those versions can be detected without
// specifying the absolute directory. For a newer version (>= ~2018), this won't
// work. The newer graphics drivers are located in
// c:\windows\system32\DriverStore\FileRepository\xxx.infxxx which contains a
// different number at each installation
bool BadAMDVulkanDriverVersion() {
// 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"));
std::unique_ptr<FileVersionInfoWin> file_version_info =
FileVersionInfoWin::CreateFileVersionInfoWin(kAmdDriver64);
if (!file_version_info) {
file_version_info =
FileVersionInfoWin::CreateFileVersionInfoWin(kAmdDriver32);
if (!file_version_info)
return false;
}
base::Version amd_version = file_version_info->GetFileVersion();
// 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.
const base::Version kBadAMDVulkanDriverVersion("1.0.54.0");
// CompareTo() returns -1, 0, 1 for <, ==, >.
if (amd_version.CompareTo(kBadAMDVulkanDriverVersion) != 1)
return true;
return false;
}
// Vulkan 1.1 was released by the Khronos Group on March 7, 2018.
// Blocklist all driver versions without Vulkan 1.1 support and those that cause
// lots of crashes.
bool BadGraphicsDriverVersions(const gpu::GPUInfo::GPUDevice& gpu_device) {
// GPU Device info is not available in gpu_integration_test.info-collection
// with --no-delay-for-dx12-vulkan-info-collection.
if (gpu_device.driver_version.empty())
return false;
base::Version driver_version(gpu_device.driver_version);
if (!driver_version.IsValid())
return true;
// AMD Vulkan drivers - amdvlk64.dll
constexpr uint32_t kAMDVendorId = 0x1002;
if (gpu_device.vendor_id == kAMDVendorId) {
// 26.20.12028.2 (2019)- number of crashes 1,188,048 as of 5/14/2020.
// Returns -1, 0, 1 for <, ==, >.
if (driver_version.CompareTo(base::Version("26.20.12028.2")) == 0)
return true;
}
return false;
}
bool InitVulkan(base::NativeLibrary* vulkan_library,
PFN_vkGetInstanceProcAddr* vkGetInstanceProcAddr,
PFN_vkCreateInstance* vkCreateInstance,
uint32_t* vulkan_version) {
*vulkan_version = 0;
*vulkan_library =
base::LoadNativeLibrary(base::FilePath(L"vulkan-1.dll"), nullptr);
if (!(*vulkan_library)) {
return false;
}
*vkGetInstanceProcAddr = reinterpret_cast<PFN_vkGetInstanceProcAddr>(
base::GetFunctionPointerFromNativeLibrary(*vulkan_library,
"vkGetInstanceProcAddr"));
if (*vkGetInstanceProcAddr) {
*vulkan_version = VK_MAKE_VERSION(1, 0, 0);
PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion;
vkEnumerateInstanceVersion =
reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
(*vkGetInstanceProcAddr)(nullptr, "vkEnumerateInstanceVersion"));
// If the vkGetInstanceProcAddr returns nullptr for
// vkEnumerateInstanceVersion, it is a Vulkan 1.0 implementation.
if (!vkEnumerateInstanceVersion) {
return false;
}
// Return value can be VK_SUCCESS or VK_ERROR_OUT_OF_HOST_MEMORY.
if (vkEnumerateInstanceVersion(vulkan_version) != VK_SUCCESS) {
return false;
}
// The minimum version required for Vulkan to be enabled is 1.1.0.
// No further queries will be called for early versions. They are unstable
// and might cause crashes.
if (*vulkan_version < VK_MAKE_VERSION(1, 1, 0)) {
return false;
}
*vkCreateInstance = reinterpret_cast<PFN_vkCreateInstance>(
(*vkGetInstanceProcAddr)(nullptr, "vkCreateInstance"));
if (*vkCreateInstance)
return true;
}
// From the crash reports, unloading the library here might cause a crash in
// the Vulkan loader or in the Vulkan driver. To work around it, don't
// explicitly unload the DLL. Instead, GPU process shutdown will unload all
// loaded DLLs.
// base::UnloadNativeLibrary(*vulkan_library);
return false;
}
bool InitVulkanInstanceProc(
const VkInstance& vk_instance,
const PFN_vkGetInstanceProcAddr& vkGetInstanceProcAddr,
PFN_vkEnumeratePhysicalDevices* vkEnumeratePhysicalDevices,
PFN_vkEnumerateDeviceExtensionProperties*
vkEnumerateDeviceExtensionProperties) {
*vkEnumeratePhysicalDevices =
reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(
vkGetInstanceProcAddr(vk_instance, "vkEnumeratePhysicalDevices"));
*vkEnumerateDeviceExtensionProperties =
reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(
vkGetInstanceProcAddr(vk_instance,
"vkEnumerateDeviceExtensionProperties"));
if ((*vkEnumeratePhysicalDevices) &&
(*vkEnumerateDeviceExtensionProperties)) {
return true;
}
return false;
}
uint32_t GetGpuSupportedVulkanVersion(
const gpu::GPUInfo::GPUDevice& gpu_device) {
TRACE_EVENT0("gpu", "GetGpuSupportedVulkanVersion");
base::NativeLibrary vulkan_library;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
PFN_vkCreateInstance vkCreateInstance;
PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices;
PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties;
VkInstance vk_instance = VK_NULL_HANDLE;
uint32_t physical_device_count = 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.
// Detected by the file version of amdvlk64.dll.
if (BadAMDVulkanDriverVersion()) {
return 0;
}
// Don't collect any info if the graphics vulkan driver is blocklisted or
// doesn't support Vulkan 1.1
// Detected by the graphic driver version returned by DXGI
if (BadGraphicsDriverVersions(gpu_device))
return 0;
// Only supports a version >= 1.1.0.
uint32_t vulkan_version = 0;
if (!InitVulkan(&vulkan_library, &vkGetInstanceProcAddr, &vkCreateInstance,
&vulkan_version)) {
return 0;
}
VkApplicationInfo app_info = {};
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
const std::vector<const char*> enabled_instance_extensions = {
"VK_KHR_surface", "VK_KHR_win32_surface"};
VkInstanceCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.pApplicationInfo = &app_info;
create_info.enabledExtensionCount = enabled_instance_extensions.size();
create_info.ppEnabledExtensionNames = enabled_instance_extensions.data();
// Get the Vulkan API version supported in the GPU driver
int highest_minor_version = VK_VERSION_MINOR(vulkan_version);
for (int minor_version = highest_minor_version; minor_version >= 1;
--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,
&vkEnumeratePhysicalDevices,
&vkEnumerateDeviceExtensionProperties)) {
result = vkEnumeratePhysicalDevices(vk_instance, &physical_device_count,
nullptr);
if (result == VK_SUCCESS && physical_device_count > 0) {
return app_info.apiVersion;
} else {
// Skip destroy here. GPU process shutdown will unload all loaded DLLs.
// vkDestroyInstance(vk_instance, nullptr);
vk_instance = VK_NULL_HANDLE;
}
}
}
// From the crash reports, calling the following two functions might cause a
// crash in the Vulkan loader or in the Vulkan driver. To work around it,
// don't explicitly unload the DLL. Instead, GPU process shutdown will unload
// all loaded DLLs.
// if (vk_instance) {
// vkDestroyInstance(vk_instance, nullptr);
// }
// base::UnloadNativeLibrary(vulkan_library);
return 0;
}
void RecordGpuSupportedDx12VersionHistograms(
uint32_t d3d12_feature_level,
uint32_t highest_shader_model_version) {
bool supports_dx12 =
(d3d12_feature_level >= D3D_FEATURE_LEVEL_12_0) ? true : false;
UMA_HISTOGRAM_BOOLEAN("GPU.SupportsDX12", supports_dx12);
UMA_HISTOGRAM_ENUMERATION(
"GPU.D3D12FeatureLevel",
ConvertToHistogramFeatureLevel(d3d12_feature_level));
UMA_HISTOGRAM_ENUMERATION(
"GPU.D3D12HighestShaderModel",
ConvertToHistogramShaderVersion(highest_shader_model_version));
}
bool CollectD3D11FeatureInfo(D3D_FEATURE_LEVEL* d3d11_feature_level,
bool* has_discrete_gpu) {
Microsoft::WRL::ComPtr<IDXGIFactory1> dxgi_factory;
if (FAILED(::CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_factory))))
return false;
base::ScopedNativeLibrary d3d11_library(
base::FilePath(FILE_PATH_LITERAL("d3d11.dll")));
if (!d3d11_library.is_valid())
return false;
PFN_D3D11_CREATE_DEVICE D3D11CreateDevice =
reinterpret_cast<PFN_D3D11_CREATE_DEVICE>(
d3d11_library.GetFunctionPointer("D3D11CreateDevice"));
if (!D3D11CreateDevice)
return false;
// The order of feature levels to attempt to create in D3D CreateDevice
const D3D_FEATURE_LEVEL kFeatureLevels[] = {
D3D_FEATURE_LEVEL_12_1, D3D_FEATURE_LEVEL_12_0, D3D_FEATURE_LEVEL_11_1,
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};
bool detected_discrete_gpu = false;
D3D_FEATURE_LEVEL max_level = D3D_FEATURE_LEVEL_1_0_CORE;
Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
for (UINT ii = 0; SUCCEEDED(dxgi_factory->EnumAdapters(ii, &dxgi_adapter));
++ii) {
DXGI_ADAPTER_DESC desc;
if (SUCCEEDED(dxgi_adapter->GetDesc(&desc)) && desc.VendorId == 0x1414) {
// Bypass Microsoft software renderer.
continue;
}
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device;
D3D_FEATURE_LEVEL returned_feature_level = D3D_FEATURE_LEVEL_1_0_CORE;
if (FAILED(D3D11CreateDevice(dxgi_adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN,
/*Software=*/0,
/*Flags=*/0, kFeatureLevels,
_countof(kFeatureLevels), D3D11_SDK_VERSION,
&d3d11_device, &returned_feature_level,
/*ppImmediateContext=*/nullptr))) {
continue;
}
if (returned_feature_level > max_level)
max_level = returned_feature_level;
Microsoft::WRL::ComPtr<ID3D11Device3> d3d11_device_3;
if (FAILED(d3d11_device.As(&d3d11_device_3)))
continue;
D3D11_FEATURE_DATA_D3D11_OPTIONS2 data = {};
if (FAILED(d3d11_device_3->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS2,
&data, sizeof(data)))) {
continue;
}
if (!data.UnifiedMemoryArchitecture)
detected_discrete_gpu = true;
}
if (max_level > D3D_FEATURE_LEVEL_1_0_CORE) {
*d3d11_feature_level = max_level;
*has_discrete_gpu = detected_discrete_gpu;
return true;
}
return false;
}
bool CollectContextGraphicsInfo(GPUInfo* gpu_info) {
TRACE_EVENT0("gpu", "CollectGraphicsInfo");
DCHECK(gpu_info);
if (!CollectGraphicsInfoGL(gpu_info))
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);
// TODO(zmo): we only need to call CollectDriverInfoD3D() if we use ANGLE.
return CollectDriverInfoD3D(gpu_info);
}
bool IdentifyActiveGPUWithLuid(GPUInfo* gpu_info) {
LUID luid;
if (!GetActiveAdapterLuid(&luid))
return false;
gpu_info->gpu.active = false;
for (size_t i = 0; i < gpu_info->secondary_gpus.size(); i++)
gpu_info->secondary_gpus[i].active = false;
if (gpu_info->gpu.luid.HighPart == luid.HighPart &&
gpu_info->gpu.luid.LowPart == luid.LowPart) {
gpu_info->gpu.active = true;
return true;
}
for (size_t i = 0; i < gpu_info->secondary_gpus.size(); i++) {
if (gpu_info->secondary_gpus[i].luid.HighPart == luid.HighPart &&
gpu_info->secondary_gpus[i].luid.LowPart == luid.LowPart) {
gpu_info->secondary_gpus[i].active = true;
return true;
}
}
return false;
}
} // namespace gpu