Edit

kc3-lang/angle/src/gpu_info_util/SystemInfo_win.cpp

Branch :

  • Show log

    Commit

  • Author : Corentin Wallez
    Date : 2017-09-05 15:11:06
    Hash : 1a1ae0f8
    Message : SystemInfo: Find primary with EnumDisplayDevices Without DXGI, EnumDisplayDevices is the only way to get the primary device. Previously the code would work on most configs with a combination of AMD, Intel and NVIDIA GPUs but would fail on more esoteric system. Like our try bots that have Matrox GPUs. BUG=angleproject:1874 BUG=angleproject:2137 Change-Id: Ie2dfbb559001ccad2fd5b8a8fd6436e0fba9d003 Reviewed-on: https://chromium-review.googlesource.com/651629 Reviewed-by: Geoff Lang <geofflang@chromium.org>

  • src/gpu_info_util/SystemInfo_win.cpp
  • //
    // 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 <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 <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
    {
    
    // 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;
        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)
    {
        // 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<int>(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