| // 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 |