// // 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" #include "common/debug.h" #include "common/string_utils.h" // Windows.h needs to be included first #include #if defined(GPU_INFO_USE_SETUPAPI) // Remove parts of commctrl.h that have compile errors #define NOTOOLBAR #define NOTOOLTIPS #include #include #elif defined(GPU_INFO_USE_DXGI) #include #include #else #error "SystemInfo_win needs at least GPU_INFO_USE_SETUPAPI or GPU_INFO_USE_DXGI defined" #endif #include #include namespace angle { namespace { // Returns the CM device ID of the primary GPU. std::string GetPrimaryDisplayDeviceId() { DISPLAY_DEVICEA displayDevice; displayDevice.cb = sizeof(DISPLAY_DEVICEA); for (int i = 0; EnumDisplayDevicesA(nullptr, i, &displayDevice, 0); ++i) { if (displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) { return displayDevice.DeviceID; } } return ""; } #if defined(GPU_INFO_USE_SETUPAPI) std::string GetRegistryStringValue(HKEY key, const char *valueName) { std::array value; DWORD valueSize = sizeof(value); if (RegQueryValueExA(key, valueName, nullptr, nullptr, reinterpret_cast(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 *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 {}/<4 digit number>. std::array value; if (!SetupDiGetDeviceRegistryPropertyW(deviceInfo, &deviceData, SPDRP_DRIVER, nullptr, reinterpret_cast(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 *devices) { IDXGIFactory *factory; if (!SUCCEEDED(CreateDXGIFactory(__uuidof(IDXGIFactory), reinterpret_cast(&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) { // Get the CM device ID first so that it is returned even in error cases. info->primaryDisplayDeviceId = GetPrimaryDisplayDeviceId(); #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); // Override the primary GPU index with what we gathered from EnumDisplayDevices uint32_t primaryVendorId = 0; uint32_t primaryDeviceId = 0; if (!CMDeviceIDToDeviceAndVendorID(info->primaryDisplayDeviceId, &primaryVendorId, &primaryDeviceId)) { return false; } bool foundPrimary = false; for (size_t i = 0; i < info->gpus.size(); ++i) { if (info->gpus[i].vendorId == primaryVendorId && info->gpus[i].deviceId == primaryDeviceId) { info->primaryGPUIndex = static_cast(i); foundPrimary = true; } } ASSERT(foundPrimary); // nvd3d9wrap.dll is loaded into all processes when Optimus is enabled. HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll"); info->isOptimus = nvd3d9wrap != nullptr; return true; } } // namespace angle