blob: 30738224bf2964ebbbe502e2eb0aaa82b6dfadaa [file] [log] [blame]
//
// 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>
#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;
if (!SUCCEEDED(CreateDXGIFactory(__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.
HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll");
info->isOptimus = nvd3d9wrap != nullptr;
return true;
}
} // namespace angle