| // |
| // Copyright (c) 2013-2017 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // SystemInfo_win.cpp: implementation of the Windows-specific parts of SystemInfo.h |
| |
| #include "gpu_info_util/SystemInfo_internal.h" |
| |
| // Windows.h needs to be included first |
| #include <windows.h> |
| |
| #if defined(GPU_INFO_USE_SETUPAPI) |
| #include <cfgmgr32.h> |
| #include <setupapi.h> |
| #elif defined(GPU_INFO_USE_DXGI) |
| #include <dxgi.h> |
| #include <d3d10.h> |
| #define __uuidof(NAME) IID_##NAME |
| |
| #else |
| #error "SystemInfo_win needs at least GPU_INFO_USE_SETUPAPI or GPU_INFO_USE_DXGI defined" |
| #endif |
| |
| #include <array> |
| #include <sstream> |
| |
| namespace angle |
| { |
| |
| namespace |
| { |
| |
| #if defined(GPU_INFO_USE_SETUPAPI) |
| |
| std::string GetRegistryStringValue(HKEY key, const char *valueName) |
| { |
| std::array<char, 255> value; |
| DWORD valueSize = sizeof(value); |
| if (RegQueryValueExA(key, valueName, nullptr, nullptr, reinterpret_cast<LPBYTE>(value.data()), |
| &valueSize) == ERROR_SUCCESS) |
| { |
| return value.data(); |
| } |
| return ""; |
| } |
| |
| // Gathers information about the devices from the registry. The reason why we aren't using |
| // a dedicated API such as DXGI is that we need information like the driver vendor and date. |
| // DXGI doesn't provide a way to know the device registry key from an IDXGIAdapter. |
| bool GetDevicesFromRegistry(std::vector<GPUDeviceInfo> *devices) |
| { |
| // Display adapter class GUID from |
| // https://msdn.microsoft.com/en-us/library/windows/hardware/ff553426%28v=vs.85%29.aspx |
| GUID displayClass = { |
| 0x4d36e968, 0xe325, 0x11ce, {0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}}; |
| |
| HDEVINFO deviceInfo = SetupDiGetClassDevsW(&displayClass, nullptr, nullptr, DIGCF_PRESENT); |
| |
| if (deviceInfo == INVALID_HANDLE_VALUE) |
| { |
| return false; |
| } |
| |
| // This iterates over the devices of the "Display adapter" class |
| DWORD deviceIndex = 0; |
| SP_DEVINFO_DATA deviceData; |
| deviceData.cbSize = sizeof(deviceData); |
| while (SetupDiEnumDeviceInfo(deviceInfo, deviceIndex++, &deviceData)) |
| { |
| // The device and vendor IDs can be gathered directly, but information about the driver |
| // requires some registry digging |
| char fullDeviceID[MAX_DEVICE_ID_LEN]; |
| if (CM_Get_Device_IDA(deviceData.DevInst, fullDeviceID, MAX_DEVICE_ID_LEN, 0) != CR_SUCCESS) |
| { |
| continue; |
| } |
| |
| GPUDeviceInfo device; |
| |
| if (!CMDeviceIDToDeviceAndVendorID(fullDeviceID, &device.vendorId, &device.deviceId)) |
| { |
| continue; |
| } |
| |
| // The driver key will end with something like {<displayClass>}/<4 digit number>. |
| std::array<WCHAR, 255> value; |
| if (!SetupDiGetDeviceRegistryPropertyW(deviceInfo, &deviceData, SPDRP_DRIVER, nullptr, |
| reinterpret_cast<PBYTE>(value.data()), sizeof(value), |
| nullptr)) |
| { |
| continue; |
| } |
| |
| std::wstring driverKey = L"System\\CurrentControlSet\\Control\\Class\\"; |
| driverKey += value.data(); |
| |
| HKEY key; |
| if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, driverKey.c_str(), 0, KEY_QUERY_VALUE, &key) != |
| ERROR_SUCCESS) |
| { |
| continue; |
| } |
| |
| device.driverVersion = GetRegistryStringValue(key, "DriverVersion"); |
| device.driverDate = GetRegistryStringValue(key, "DriverDate"); |
| device.driverVendor = GetRegistryStringValue(key, "ProviderName"); |
| |
| RegCloseKey(key); |
| |
| devices->push_back(device); |
| } |
| |
| SetupDiDestroyDeviceInfoList(deviceInfo); |
| |
| return true; |
| } |
| |
| #elif defined(GPU_INFO_USE_DXGI) |
| |
| bool GetDevicesFromDXGI(std::vector<GPUDeviceInfo> *devices) |
| { |
| IDXGIFactory *factory; |
| // The CreateDXGIFactory function does not exist for Windows Store apps. |
| // Instead, Windows Store apps use the CreateDXGIFactory1 function. |
| if (!SUCCEEDED(CreateDXGIFactory1(__uuidof(IDXGIFactory), reinterpret_cast<void **>(&factory)))) |
| { |
| return false; |
| } |
| |
| UINT i = 0; |
| IDXGIAdapter *adapter = nullptr; |
| while (factory->EnumAdapters(i++, &adapter) != DXGI_ERROR_NOT_FOUND) |
| { |
| DXGI_ADAPTER_DESC desc; |
| adapter->GetDesc(&desc); |
| |
| LARGE_INTEGER umdVersion; |
| if (adapter->CheckInterfaceSupport(__uuidof(ID3D10Device), &umdVersion) == |
| DXGI_ERROR_UNSUPPORTED) |
| { |
| adapter->Release(); |
| continue; |
| } |
| |
| // The UMD driver version here is the same as in the registry except for the last number. |
| uint64_t intVersion = umdVersion.QuadPart; |
| std::ostringstream o; |
| |
| const uint64_t kMask = 0xFF; |
| o << ((intVersion >> 48) & kMask) << "."; |
| o << ((intVersion >> 32) & kMask) << "."; |
| o << ((intVersion >> 16) & kMask) << "."; |
| o << (intVersion & kMask); |
| |
| GPUDeviceInfo device; |
| device.vendorId = desc.VendorId; |
| device.deviceId = desc.DeviceId; |
| device.driverVersion = o.str(); |
| |
| devices->push_back(device); |
| |
| adapter->Release(); |
| } |
| |
| factory->Release(); |
| |
| return true; |
| } |
| |
| #else |
| #error |
| #endif |
| |
| } // anonymous namespace |
| |
| bool GetSystemInfo(SystemInfo *info) |
| { |
| #if defined(GPU_INFO_USE_SETUPAPI) |
| if (!GetDevicesFromRegistry(&info->gpus)) |
| { |
| return false; |
| } |
| #elif defined(GPU_INFO_USE_DXGI) |
| if (!GetDevicesFromDXGI(&info->gpus)) |
| { |
| return false; |
| } |
| #else |
| #error |
| #endif |
| |
| if (info->gpus.size() == 0) |
| { |
| return false; |
| } |
| |
| FindPrimaryGPU(info); |
| |
| // nvd3d9wrap.dll is loaded into all processes when Optimus is enabled. |
| // GetModuleHandleW is desktop apps only. |
| #if defined(WINAPI_FAMILY) && WINAPI_FAMILY==WINAPI_FAMILY_APP |
| info->isOptimus = false; |
| #else |
| HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll"); |
| info->isOptimus = nvd3d9wrap != nullptr; |
| #endif |
| |
| |
| return true; |
| } |
| |
| } // namespace angle |