Commit 66e4850d by Jonah Ryan-Davis Committed by Commit Bot

Use EnumAdapters to properly detect primary GPU on Win

EnumDisplayDevicesA returns the card that's connected to the display, but EnumAdapters return the adapter which the desktop primary is displayed at index 0. We can use this to determine the device used for graphics. Also cleans up the discrepancy between platforms on finding "primary" vs "active" GPU. Asserts that the GPU expected to run ANGLE commands is the active GPU, and deprecates the primary GPU to be equal to the active GPU. Bug: angleproject:3383 Change-Id: I422fba1bbe47d85b7c09e378d559eaebf89e2625 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1584360 Commit-Queue: Jonah Ryan-Davis <jonahr@google.com> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent c22ef61f
......@@ -289,7 +289,10 @@ config("angle_gpu_info_util_config") {
}
angle_static_library("angle_gpu_info_util") {
public_configs += [ ":angle_gpu_info_util_config" ]
public_configs += [
":angle_gpu_info_util_config",
":angle_backend_config",
]
public_deps = [
":angle_common",
]
......@@ -305,7 +308,7 @@ angle_static_library("angle_gpu_info_util") {
if (is_win) {
sources += libangle_gpu_info_util_win_sources
libs += [ "setupapi.lib" ]
defines += [ "GPU_INFO_USE_SETUPAPI" ]
libs += [ "dxgi.lib" ]
}
if (is_linux || is_fuchsia) {
......@@ -443,14 +446,11 @@ angle_source_set("angle_version") {
]
}
config("libANGLE_config") {
cflags = []
config("angle_backend_config") {
defines = []
libs = []
ldflags = []
if (angle_enable_d3d9) {
defines += [ "ANGLE_ENABLE_D3D9" ]
ldflags += [ "/DELAYLOAD:d3d9.dll" ]
}
if (angle_enable_d3d11) {
defines += [ "ANGLE_ENABLE_D3D11" ]
......@@ -473,6 +473,16 @@ config("libANGLE_config") {
if (angle_enable_null) {
defines += [ "ANGLE_ENABLE_NULL" ]
}
}
config("libANGLE_config") {
cflags = []
defines = []
libs = []
ldflags = []
if (angle_enable_d3d9) {
ldflags += [ "/DELAYLOAD:d3d9.dll" ]
}
defines += [ "LIBANGLE_IMPLEMENTATION" ]
if (is_win) {
......@@ -665,7 +675,10 @@ angle_static_library("libANGLE") {
}
configs += [ ":debug_annotations_config" ]
public_configs += [ ":libANGLE_config" ]
public_configs += [
":libANGLE_config",
":angle_backend_config",
]
# Windows ARM64 is available since 10.0.16299 so no need to copy
# d3dcompiler_47.dll because this file is available as inbox.
......
......@@ -238,12 +238,12 @@ bool CMDeviceIDToDeviceAndVendorID(const std::string &id, uint32_t *vendorId, ui
return success;
}
void FindPrimaryGPU(SystemInfo *info)
void FindActiveGPU(SystemInfo *info)
{
ASSERT(!info->gpus.empty());
// On dual-GPU systems we assume the non-Intel GPU is the primary one.
int primary = 0;
// On dual-GPU systems we assume the non-Intel GPU is the graphics one.
int active = 0;
bool hasIntel = false;
for (size_t i = 0; i < info->gpus.size(); ++i)
{
......@@ -251,16 +251,17 @@ void FindPrimaryGPU(SystemInfo *info)
{
hasIntel = true;
}
if (IsIntel(info->gpus[primary].vendorId))
if (IsIntel(info->gpus[active].vendorId))
{
primary = static_cast<int>(i);
active = static_cast<int>(i);
}
}
// Assume that a combination of NVIDIA or AMD with Intel means Optimus or AMD Switchable
info->primaryGPUIndex = primary;
info->isOptimus = hasIntel && IsNVIDIA(info->gpus[primary].vendorId);
info->isAMDSwitchable = hasIntel && IsAMD(info->gpus[primary].vendorId);
info->activeGPUIndex = active;
info->primaryGPUIndex = active;
info->isOptimus = hasIntel && IsNVIDIA(info->gpus[active].vendorId);
info->isAMDSwitchable = hasIntel && IsAMD(info->gpus[active].vendorId);
}
void PrintSystemInfo(const SystemInfo &info)
......@@ -289,7 +290,6 @@ void PrintSystemInfo(const SystemInfo &info)
std::cout << "\n";
std::cout << "Active GPU: " << info.activeGPUIndex << "\n";
std::cout << "Primary GPU: " << info.primaryGPUIndex << "\n";
std::cout << "\n";
std::cout << "Optimus: " << (info.isOptimus ? "true" : "false") << "\n";
......@@ -308,10 +308,6 @@ void PrintSystemInfo(const SystemInfo &info)
{
std::cout << "Machine Model Version: " << info.machineModelVersion << "\n";
}
if (!info.primaryDisplayDeviceId.empty())
{
std::cout << "Primary Display Device: " << info.primaryDisplayDeviceId << "\n";
}
std::cout << std::endl;
}
} // namespace angle
......@@ -58,13 +58,13 @@ struct SystemInfo
std::vector<GPUDeviceInfo> gpus;
// Index of the primary GPU (the discrete one on dual GPU systems) in `gpus`.
// Will never be -1 after a successful GetSystemInfo.
int primaryGPUIndex = -1;
// Index of the currently active GPU in `gpus`, can be -1 if the active GPU could not be
// detected.
// Index of the GPU expected to be used for 3D graphics. Based on a best-guess heuristic on
// some platforms. On windows, this is accurate.
int activeGPUIndex = -1;
// Deprecated, same as activeGPUIndex
int primaryGPUIndex = -1;
bool isOptimus = false;
bool isAMDSwitchable = false;
// Only true on dual-GPU Mac laptops.
......@@ -78,9 +78,6 @@ struct SystemInfo
// Only available on macOS
std::string machineModelVersion;
// Only available on Windows, set even on failure.
std::string primaryDisplayDeviceId;
};
// Gathers information about the system without starting a GPU driver and returns them in `info`.
......
......@@ -29,9 +29,9 @@ bool ParseMacMachineModel(const std::string &identifier,
int32_t *minor);
bool CMDeviceIDToDeviceAndVendorID(const std::string &id, uint32_t *vendorId, uint32_t *deviceId);
// In the case there are multiple GPUs, this finds the primary one and sets Optimus or AMD
// Switchable
void FindPrimaryGPU(SystemInfo *info);
// Use a heuristic to attempt to find the GPU used for 3D graphics. Sets activeGPUIndex,
// isOptimus, and isAMDSwitchable. Deprecated: also sets primaryGPUIndex.
void FindActiveGPU(SystemInfo *info);
} // namespace angle
......
......@@ -81,7 +81,7 @@ bool GetSystemInfo(SystemInfo *info)
return false;
}
FindPrimaryGPU(info);
FindActiveGPU(info);
for (size_t i = 0; i < info->gpus.size(); ++i)
{
......@@ -118,9 +118,9 @@ bool GetSystemInfo(SystemInfo *info)
}
}
// In dual-GPU cases the PCI scan sometimes only gives us the Intel GPU.
// If we are able to query for the Nvidia driver version, it means there
// was hidden Nvidia GPU, so we add it to the list and make it primary.
// In dual-GPU cases the PCI scan sometimes only gives us the Intel GPU. If we are able to
// query for the Nvidia driver version, it means there was hidden Nvidia GPU, so we add it
// to the list.
if (IsIntel(gpu->vendorId) && info->gpus.size() == 1)
{
std::string version;
......
......@@ -70,22 +70,6 @@ bool GetEntryProperty(io_registry_entry_t entry, CFStringRef name, uint32_t *val
return true;
}
// CGDisplayIOServicePort is deprecated as of macOS 10.9, but has no replacement, see
// https://crbug.com/650837
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// Find the info of the current GPU.
bool GetActiveGPU(VendorID *vendorId, DeviceID *deviceId)
{
io_registry_entry_t port = CGDisplayIOServicePort(kCGDirectMainDisplay);
return GetEntryProperty(port, CFSTR("vendor-id"), vendorId) &&
GetEntryProperty(port, CFSTR("device-id"), deviceId);
}
#pragma clang diagnostic pop
// Gathers the vendor and device IDs for the PCI GPUs
bool GetPCIDevices(std::vector<GPUDeviceInfo> *devices)
{
......@@ -143,26 +127,7 @@ bool GetSystemInfo(SystemInfo *info)
return false;
}
// Find the active GPU
{
VendorID activeVendor;
DeviceID activeDevice;
if (!GetActiveGPU(&activeVendor, &activeDevice))
{
return false;
}
for (size_t i = 0; i < info->gpus.size(); ++i)
{
if (info->gpus[i].vendorId == activeVendor && info->gpus[i].deviceId == activeDevice)
{
info->activeGPUIndex = i;
break;
}
}
}
FindPrimaryGPU(info);
FindActiveGPU(info);
// Figure out whether this is a dual-GPU system.
//
......
......@@ -14,18 +14,8 @@
// Windows.h needs to be included first
#include <windows.h>
#if defined(GPU_INFO_USE_SETUPAPI)
// Remove parts of commctrl.h that have compile errors
# define NOTOOLBAR
# define NOTOOLTIPS
# include <cfgmgr32.h>
# include <setupapi.h>
#elif defined(GPU_INFO_USE_DXGI)
# include <d3d10.h>
# include <dxgi.h>
#else
# error "SystemInfo_win needs at least GPU_INFO_USE_SETUPAPI or GPU_INFO_USE_DXGI defined"
#endif
#include <d3d10.h>
#include <dxgi.h>
#include <array>
#include <sstream>
......@@ -36,110 +26,6 @@ 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<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;
......@@ -185,63 +71,33 @@ bool GetDevicesFromDXGI(std::vector<GPUDeviceInfo> *devices)
factory->Release();
return true;
return (i > 0);
}
#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;
// Call FindActiveGPU to populate activeGPUIndex, isOptimus, and isAMDSwitchable.
FindActiveGPU(info);
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<int>(i);
foundPrimary = true;
}
}
ASSERT(foundPrimary);
// Override activeGPUIndex. The first index returned by EnumAdapters is the active GPU. We
// can override the heuristic to find the active GPU
info->activeGPUIndex = 0;
// Deprecated: set primaryGPUIndex to the same index.
info->primaryGPUIndex = 0;
// nvd3d9wrap.dll is loaded into all processes when Optimus is enabled.
// Override isOptimus. nvd3d9wrap.dll is loaded into all processes when Optimus is enabled.
HMODULE nvd3d9wrap = GetModuleHandleW(L"nvd3d9wrap.dll");
info->isOptimus = nvd3d9wrap != nullptr;
......
......@@ -541,6 +541,7 @@ if (build_angle_gles1_conform_tests) {
configs += [
"${angle_root}:libANGLE_config",
"${angle_root}:library_name_config",
"${angle_root}:angle_backend_config",
]
if (is_android) {
......
......@@ -331,9 +331,9 @@ inline bool GetGPUTestSystemInfo(SystemInfo **sysInfo)
return sPopulated;
}
// Get the primary GPUDeviceInfo from the SystemInfo struct.
// Returns false if devInfo is not guaranteed to be set to the primary device.
inline bool GetPrimaryGPU(GPUDeviceInfo **devInfo)
// Get the active GPUDeviceInfo from the SystemInfo struct.
// Returns false if devInfo is not guaranteed to be set to the active device.
inline bool GetActiveGPU(GPUDeviceInfo **devInfo)
{
SystemInfo *systemInfo = nullptr;
GetGPUTestSystemInfo(&systemInfo);
......@@ -343,24 +343,24 @@ inline bool GetPrimaryGPU(GPUDeviceInfo **devInfo)
}
// Default to the first index
uint32_t index = 0;
// See if the primaryGPUIndex was set first
if (systemInfo->primaryGPUIndex != -1)
// See if the activeGPUIndex was set first
if (systemInfo->activeGPUIndex != -1)
{
index = systemInfo->primaryGPUIndex;
index = systemInfo->activeGPUIndex;
}
ASSERT(index < systemInfo->gpus.size());
*devInfo = &(systemInfo->gpus[index]);
return true;
}
// Get the vendor ID of the primary GPU from the SystemInfo struct.
// Get the vendor ID of the active GPU from the SystemInfo struct.
// Returns 0 if there is an error.
inline VendorID GetPrimaryGPUVendorID()
inline VendorID GetActiveGPUVendorID()
{
GPUDeviceInfo *primaryGPU = nullptr;
if (GetPrimaryGPU(&primaryGPU))
GPUDeviceInfo *activeGPU = nullptr;
if (GetActiveGPU(&activeGPU))
{
return primaryGPU->vendorId;
return activeGPU->vendorId;
}
else
{
......@@ -368,14 +368,14 @@ inline VendorID GetPrimaryGPUVendorID()
}
}
// Get the device ID of the primary GPU from the SystemInfo struct.
// Get the device ID of the active GPU from the SystemInfo struct.
// Returns 0 if there is an error.
inline DeviceID GetPrimaryGPUDeviceID()
inline DeviceID GetActiveGPUDeviceID()
{
GPUDeviceInfo *primaryGPU = nullptr;
if (GetPrimaryGPU(&primaryGPU))
GPUDeviceInfo *activeGPU = nullptr;
if (GetActiveGPU(&activeGPU))
{
return primaryGPU->deviceId;
return activeGPU->deviceId;
}
else
{
......@@ -383,28 +383,28 @@ inline DeviceID GetPrimaryGPUDeviceID()
}
}
// Check whether the primary GPU is NVIDIA.
// Check whether the active GPU is NVIDIA.
inline bool IsNVIDIA()
{
return angle::IsNVIDIA(GetPrimaryGPUVendorID());
return angle::IsNVIDIA(GetActiveGPUVendorID());
}
// Check whether the primary GPU is AMD.
// Check whether the active GPU is AMD.
inline bool IsAMD()
{
return angle::IsAMD(GetPrimaryGPUVendorID());
return angle::IsAMD(GetActiveGPUVendorID());
}
// Check whether the primary GPU is Intel.
// Check whether the active GPU is Intel.
inline bool IsIntel()
{
return angle::IsIntel(GetPrimaryGPUVendorID());
return angle::IsIntel(GetActiveGPUVendorID());
}
// Check whether the primary GPU is VMWare.
// Check whether the active GPU is VMWare.
inline bool IsVMWare()
{
return angle::IsVMWare(GetPrimaryGPUVendorID());
return angle::IsVMWare(GetActiveGPUVendorID());
}
// Check whether this is a debug build.
......@@ -460,7 +460,7 @@ inline bool IsDeviceIdGPU(const std::string &gpuDeviceId)
// PushErrorMessage(kErrorMessage[kErrorEntryWithGpuDeviceIdConflicts], line_number);
return false;
}
return (deviceId == GetPrimaryGPUDeviceID());
return (deviceId == GetActiveGPUDeviceID());
}
// Check whether the active GPU is a NVIDIA Quadro P400
......
......@@ -191,7 +191,7 @@ bool IsNVIDIAShield()
bool IsConfigWhitelisted(const SystemInfo &systemInfo, const PlatformParameters &param)
{
VendorID vendorID = systemInfo.gpus[systemInfo.primaryGPUIndex].vendorId;
VendorID vendorID = systemInfo.gpus[systemInfo.activeGPUIndex].vendorId;
// We support the default and null back-ends on every platform.
if (param.driver == GLESDriverType::AngleEGL)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment