Commit 7b4e00d2 by Corentin Wallez Committed by Commit Bot

gpu_info_util: Implement GetSystemInfo on Linux

BUG=angleproject:1874 Change-Id: Id39c26b806e6a7937517235afe0ca60f5087df5b Reviewed-on: https://chromium-review.googlesource.com/438940Reviewed-by: 's avatarZhenyao Mo <zmo@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org>
parent 1b60d8d2
......@@ -180,6 +180,58 @@ static_library("angle_image_util") {
]
}
config("angle_gpu_info_util_config") {
include_dirs = [
"include",
"src",
]
}
static_library("angle_gpu_info_util") {
configs -= angle_undefine_configs
configs += [ ":internal_config" ]
public_configs = [ ":angle_gpu_info_util_config" ]
public_deps = [
":angle_common",
]
sources = rebase_path(gles_gypi.libangle_gpu_info_util_sources, ".", "src")
deps = []
libs = []
defines = []
if (is_linux) {
sources +=
rebase_path(gles_gypi.libangle_gpu_info_util_linux_sources, ".", "src")
if (use_x11) {
sources += rebase_path(gles_gypi.libangle_gpu_info_util_x11_sources,
".",
"src")
deps += [ "src/third_party/libXNVCtrl:libXNVCtrl" ]
defines += [ "GPU_INFO_USE_X11" ]
libs += [
"X11",
"Xi",
"Xext",
]
}
}
# Taken from gpu/config/BUILD.gn
# TODO(cwallez): this should be in a shared location
use_libpci = is_linux && (!is_chromecast || is_cast_desktop_build) &&
(use_x11 || use_ozone)
if (use_libpci) {
sources +=
rebase_path(gles_gypi.libangle_gpu_info_util_libpci_sources, ".", "src")
defines += [ "GPU_INFO_USE_LIBPCI" ]
libs += [ "pci" ]
}
}
static_library("translator") {
sources = rebase_path(compiler_gypi.angle_translator_sources, ".", "src")
defines = []
......@@ -318,6 +370,7 @@ static_library("libANGLE") {
]
deps = [
":angle_image_util",
":angle_gpu_info_util",
":commit_id",
":includes",
":translator",
......@@ -611,6 +664,7 @@ foreach(is_shared_library,
if (is_shared_library) {
configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
}
# To prevent linux sources filtering on android
set_sources_assignment_filter([])
sources += rebase_path(util_gypi.util_linux_sources, ".", "util")
......
......@@ -190,6 +190,89 @@
},
{
'target_name': 'angle_gpu_info_util',
'type': 'static_library',
'includes': [ '../gyp/common_defines.gypi', ],
'sources':
[
'<@(libangle_gpu_info_util_sources)',
],
'include_dirs':
[
'.',
'../include',
],
'dependencies':
[
'angle_common',
],
'direct_dependent_settings':
{
'include_dirs':
[
'<(angle_path)/include',
'<(angle_path)/src',
],
},
'conditions':
[
['OS=="linux"',
{
'sources':
[
'<@(libangle_gpu_info_util_linux_sources)',
],
}],
['OS=="linux" and use_x11==1',
{
'sources':
[
'<@(libangle_gpu_info_util_x11_sources)',
],
'defines':
[
'GPU_INFO_USE_X11',
],
'dependencies':
[
'<(angle_path)/src/third_party/libXNVCtrl/libXNVCtrl.gyp:libXNVCtrl',
],
'link_settings': {
'ldflags':
[
'<!@(<(pkg-config) --libs-only-L --libs-only-other x11 xi xext)',
],
'libraries':
[
'<!@(<(pkg-config) --libs-only-l x11 xi xext) -ldl',
],
},
}],
['OS=="linux" and use_libpci==1',
{
'sources':
[
'<@(libangle_gpu_info_util_libpci_sources)',
],
'defines':
[
'GPU_INFO_USE_LIBPCI',
],
'link_settings': {
'ldflags':
[
'<!@(<(pkg-config) --libs-only-L --libs-only-other libpci)',
],
'libraries':
[
'<!@(<(pkg-config) --libs-only-l libpci)',
],
},
}],
],
},
{
'target_name': 'copy_scripts',
'type': 'none',
'includes': [ '../gyp/common_defines.gypi', ],
......
//
// 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.cpp: implementation of the system-agnostic parts of SystemInfo.h
#include "gpu_info_util/SystemInfo.h"
#include <cstring>
#include <sstream>
namespace angle
{
bool IsAMD(uint32_t vendorId)
{
return vendorId == VENDOR_ID_AMD;
}
bool IsIntel(uint32_t vendorId)
{
return vendorId == VENDOR_ID_INTEL;
}
bool IsNvidia(uint32_t vendorId)
{
return vendorId == VENDOR_ID_NVIDIA;
}
bool IsQualcomm(uint32_t vendorId)
{
return vendorId == VENDOR_ID_QUALCOMM;
}
bool ParseAMDBrahmaDriverVersion(const std::string &content, std::string *version)
{
const size_t begin = content.find_first_of("0123456789");
if (begin == std::string::npos)
{
return false;
}
const size_t end = content.find_first_not_of("0123456789.", begin);
if (end == std::string::npos)
{
*version = content.substr(begin);
}
else
{
*version = content.substr(begin, end - begin);
}
return true;
}
bool ParseAMDCatalystDriverVersion(const std::string &content, std::string *version)
{
std::istringstream stream(content);
std::string line;
while (std::getline(stream, line))
{
static const char kReleaseVersion[] = "ReleaseVersion=";
if (line.compare(0, std::strlen(kReleaseVersion), kReleaseVersion) != 0)
{
continue;
}
if (ParseAMDBrahmaDriverVersion(line, version))
{
return true;
}
}
return false;
}
} // namespace angle
//
// 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.h: gathers information available without starting a GPU driver.
#ifndef GPU_INFO_UTIL_SYSTEM_INFO_H_
#define GPU_INFO_UTIL_SYSTEM_INFO_H_
#include <cstdint>
#include <string>
#include <vector>
namespace angle
{
struct GPUDeviceInfo
{
uint32_t vendorId;
uint32_t deviceId;
std::string driverVendor;
std::string driverVersion;
std::string driverDate;
};
struct SystemInfo
{
std::vector<GPUDeviceInfo> gpus;
int primaryGPUIndex;
bool isOptimus;
bool isAMDSwitchable;
std::string machineModelName;
};
bool GetSystemInfo(SystemInfo *info);
enum VendorID : uint32_t
{
VENDOR_ID_UNKNOWN = 0x0,
VENDOR_ID_AMD = 0x1002,
VENDOR_ID_INTEL = 0x8086,
VENDOR_ID_NVIDIA = 0x10DE,
// This is Qualcomm PCI Vendor ID.
// Android doesn't have a PCI bus, but all we need is a unique id.
VENDOR_ID_QUALCOMM = 0x5143,
};
bool IsAMD(uint32_t vendorId);
bool IsIntel(uint32_t vendorId);
bool IsNvidia(uint32_t vendorId);
bool IsQualcomm(uint32_t vendorId);
} // namespace angle
#endif // GPU_INFO_UTIL_SYSTEM_INFO_H_
//
// 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_internal.h: Functions used by the SystemInfo_* files and unittests
#ifndef GPU_INFO_UTIL_SYSTEM_INFO_INTERNAL_H_
#define GPU_INFO_UTIL_SYSTEM_INFO_INTERNAL_H_
#include "gpu_info_util/SystemInfo.h"
namespace angle
{
// Defined in SystemInfo_libpci when GPU_INFO_USE_LIBPCI is defined.
bool GetPCIDevicesWithLibPCI(std::vector<GPUDeviceInfo> *devices);
// Defined in SystemInfo_x11 when GPU_INFO_USE_X11 is defined.
bool GetNvidiaDriverVersionWithXNVCtrl(std::string *version);
// Target specific helper functions that can be compiled on all targets
// Live in SystemInfo.cpp
bool ParseAMDBrahmaDriverVersion(const std::string &content, std::string *version);
bool ParseAMDCatalystDriverVersion(const std::string &content, std::string *version);
} // namespace angle
#endif // GPU_INFO_UTIL_SYSTEM_INFO_INTERNAL_H_
//
// 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_libpci.cpp: implementation of the libPCI-specific parts of SystemInfo.h
#include "gpu_info_util/SystemInfo_internal.h"
#include <dlfcn.h>
#include <pci/pci.h>
#include <unistd.h>
#include "common/angleutils.h"
#include "common/debug.h"
#if !defined(GPU_INFO_USE_LIBPCI)
#error SystemInfo_libpci.cpp compiled without GPU_INFO_USE_LIBPCI
#endif
namespace angle
{
namespace
{
struct LibPCI : angle::NonCopyable
{
static bool IsSupported()
{
return access("/sys/bus/pci/", F_OK) == 0 || access("/sys/bs/pci_express/", F_OK) == 0;
}
bool Load()
{
mHandle = dlopen("libpci.so.3", RTLD_LAZY);
if (mHandle == nullptr)
{
mHandle = dlopen("libpci.so", RTLD_LAZY);
}
if (mHandle == nullptr)
{
return false;
}
return (Alloc = reinterpret_cast<decltype(Alloc)>(dlsym(mHandle, "pci_alloc"))) !=
nullptr &&
(Init = reinterpret_cast<decltype(Init)>(dlsym(mHandle, "pci_init"))) != nullptr &&
(Cleanup = reinterpret_cast<decltype(Cleanup)>(dlsym(mHandle, "pci_cleanup"))) !=
nullptr &&
(ScanBus = reinterpret_cast<decltype(ScanBus)>(dlsym(mHandle, "pci_scan_bus"))) !=
nullptr &&
(FillInfo = reinterpret_cast<decltype(FillInfo)>(dlsym(mHandle, "pci_fill_info"))) !=
nullptr &&
(LookupName = reinterpret_cast<decltype(LookupName)>(
dlsym(mHandle, "pci_lookup_name"))) != nullptr;
}
~LibPCI()
{
if (mHandle != nullptr)
{
dlclose(mHandle);
}
}
decltype(&::pci_alloc) Alloc = nullptr;
decltype(&::pci_init) Init = nullptr;
decltype(&::pci_cleanup) Cleanup = nullptr;
decltype(&::pci_scan_bus) ScanBus = nullptr;
decltype(&::pci_fill_info) FillInfo = nullptr;
decltype(&::pci_lookup_name) LookupName = nullptr;
private:
void *mHandle = nullptr;
};
} // anonymous namespace
// Adds an entry per PCI GPU found and fills the device and vendor ID.
bool GetPCIDevicesWithLibPCI(std::vector<GPUDeviceInfo> *devices)
{
if (!LibPCI::IsSupported())
{
return false;
}
LibPCI pci;
if (!pci.Load())
{
return false;
}
pci_access *access = pci.Alloc();
ASSERT(access != nullptr);
pci.Init(access);
pci.ScanBus(access);
for (pci_dev *device = access->devices; device != nullptr; device = device->next)
{
pci.FillInfo(device, PCI_FILL_IDENT | PCI_FILL_CLASS);
// Skip non-GPU devices
switch (device->device_class)
{
case PCI_CLASS_DISPLAY_VGA:
case PCI_CLASS_DISPLAY_XGA:
case PCI_CLASS_DISPLAY_3D:
break;
default:
continue;
}
// Skip unknown devices
if (device->vendor_id == 0 || device->device_id == 0)
{
continue;
}
GPUDeviceInfo info;
info.vendorId = device->vendor_id;
info.deviceId = device->device_id;
devices->push_back(info);
}
pci.Cleanup(access);
return true;
}
}
//
// 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_linux.cpp: implementation of the Linux-specific parts of SystemInfo.h
#include "gpu_info_util/SystemInfo_internal.h"
#include <cstring>
#include <fstream>
#include "common/angleutils.h"
#include "common/debug.h"
namespace angle
{
namespace
{
bool ReadWholeFile(const char *filename, std::string *content)
{
std::ifstream file(filename);
if (!file)
{
return false;
}
*content = std::string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
return true;
}
// Scan /sys/module/amdgpu/version.
bool GetAMDBrahmaDriverVersion(std::string *version)
{
*version = "";
std::string content;
return ReadWholeFile("/sys/module/amdgpu/version", &content) &&
ParseAMDBrahmaDriverVersion(content, version);
}
// Scan /etc/ati/amdpcsdb.default for "ReleaseVersion".
bool GetAMDCatalystDriverVersion(std::string *version)
{
*version = "";
std::string content;
return ReadWholeFile("/etc/ati/amdpcsdb.default", &content) &&
ParseAMDCatalystDriverVersion(content, version);
}
} // anonymous namespace
#if !defined(GPU_INFO_USE_X11)
bool GetNvidiaDriverVersionWithXNVCtrl(std::string *version)
{
return false;
}
#endif
#if !defined(GPU_INFO_USE_LIBPCI)
bool GetPCIDevicesWithLibPCI(std::vector<GPUDeviceInfo> *devices)
{
return false;
}
#endif
bool GetSystemInfo(SystemInfo *info)
{
if (!GetPCIDevicesWithLibPCI(&(info->gpus)))
{
return false;
}
if (info->gpus.size() == 0)
{
return false;
}
// On dual-GPU systems we assume the non-Intel GPU is the primary one.
int primary = 0;
bool hasIntel = false;
for (size_t i = 0; i < info->gpus.size(); ++i)
{
if (IsIntel(info->gpus[i].vendorId))
{
hasIntel = true;
}
if (IsIntel(info->gpus[primary].vendorId))
{
primary = i;
}
}
// Assume that a combination of AMD or Nvidia 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);
for (size_t i = 0; i < info->gpus.size(); ++i)
{
GPUDeviceInfo *gpu = &info->gpus[i];
// New GPUs might be added inside this loop, don't query for their driver version again
if (gpu->driverVendor.empty())
{
continue;
}
if (IsAMD(gpu->vendorId))
{
std::string version;
if (GetAMDBrahmaDriverVersion(&version))
{
gpu->driverVendor = "ATI / AMD (Brahma)";
gpu->driverVersion = std::move(version);
}
else if (GetAMDCatalystDriverVersion(&version))
{
gpu->driverVendor = "ATI / AMD (Catalyst)";
gpu->driverVersion = std::move(version);
}
}
if (IsNvidia(gpu->vendorId))
{
std::string version;
if (GetNvidiaDriverVersionWithXNVCtrl(&version))
{
gpu->driverVendor = "Nvidia";
gpu->driverVersion = std::move(version);
}
}
// 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.
if (IsIntel(gpu->vendorId) && info->gpus.size() == 1)
{
std::string version;
if (GetNvidiaDriverVersionWithXNVCtrl(&version))
{
GPUDeviceInfo nvidiaInfo;
nvidiaInfo.vendorId = VENDOR_ID_NVIDIA;
nvidiaInfo.deviceId = 0;
gpu->driverVendor = "Nvidia";
gpu->driverVersion = std::move(version);
info->gpus.emplace_back(std::move(nvidiaInfo));
info->isOptimus = true;
}
}
}
return true;
}
} // namespace angle
//
// Copyright 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_unittest.cpp: Unit tests for SystemInfo* helper functions.
//
#include "gpu_info_util/SystemInfo_internal.h"
#include <gtest/gtest.h>
using namespace angle;
namespace
{
// Test AMD Brahma driver version parsing
TEST(SystemInfoTest, AMDBrahmaVersionParsing)
{
std::string version;
// Check parsing fails when no version string is present.
ASSERT_FALSE(ParseAMDBrahmaDriverVersion("I am a lumberjack.", &version));
ASSERT_EQ("", version);
// Check parsing when the string is just the version string, with and without dots
ASSERT_TRUE(ParseAMDBrahmaDriverVersion("42", &version));
ASSERT_EQ("42", version);
ASSERT_TRUE(ParseAMDBrahmaDriverVersion("42.0.56", &version));
ASSERT_EQ("42.0.56", version);
// Check parsing with prefix / suffix
ASSERT_TRUE(ParseAMDBrahmaDriverVersion("Version=42.0.56", &version));
ASSERT_EQ("42.0.56", version);
ASSERT_TRUE(ParseAMDBrahmaDriverVersion("42.0.56 is the version", &version));
ASSERT_EQ("42.0.56", version);
ASSERT_TRUE(ParseAMDBrahmaDriverVersion("42.0.56 is the version, 111", &version));
ASSERT_EQ("42.0.56", version);
}
// Test AMD Catalyst version parsing
TEST(SystemInfoTest, AMDCatalystVersionParsing)
{
std::string version;
// Check parsing fails when no version string is present.
ASSERT_FALSE(ParseAMDCatalystDriverVersion("I am a lumberjack.\nReleaseVersion=", &version));
ASSERT_EQ("", version);
// Check parsing fails when ReleaseVersion= is present but no number appears in the line
ASSERT_FALSE(ParseAMDCatalystDriverVersion("11\nReleaseVersion=\n12", &version));
ASSERT_EQ("", version);
// Check parsing works on the simple case
ASSERT_TRUE(ParseAMDCatalystDriverVersion("ReleaseVersion=42.0.56", &version));
ASSERT_EQ("42.0.56", version);
// Check parsing works if there are other lines
ASSERT_TRUE(ParseAMDCatalystDriverVersion("11\nReleaseVersion=42.0.56\n12", &version));
ASSERT_EQ("42.0.56", version);
// Check parsing get the first version string
ASSERT_TRUE(
ParseAMDCatalystDriverVersion("ReleaseVersion=42.0.56\nReleaseVersion=0", &version));
ASSERT_EQ("42.0.56", version);
// Check parsing with prefix / suffix
ASSERT_TRUE(ParseAMDCatalystDriverVersion("ReleaseVersion=version is 42.0.56", &version));
ASSERT_EQ("42.0.56", version);
ASSERT_TRUE(ParseAMDCatalystDriverVersion("ReleaseVersion=42.0.56 is the version", &version));
ASSERT_EQ("42.0.56", version);
}
}
//
// 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_x11.cpp: implementation of the X11-specific parts of SystemInfo.h
#include "gpu_info_util/SystemInfo_internal.h"
#include <X11/Xlib.h>
#include "common/debug.h"
#include "third_party/libXNVCtrl/NVCtrl.h"
#include "third_party/libXNVCtrl/NVCtrlLib.h"
#if !defined(GPU_INFO_USE_X11)
#error SystemInfo_x11.cpp compiled without GPU_INFO_USE_X11
#endif
namespace angle
{
bool GetNvidiaDriverVersionWithXNVCtrl(std::string *version)
{
*version = "";
int eventBase = 0;
int errorBase = 0;
Display *display = XOpenDisplay(NULL);
if (XNVCTRLQueryExtension(display, &eventBase, &errorBase))
{
int screenCount = ScreenCount(display);
for (int screen = 0; screen < screenCount; ++screen)
{
char *buffer = nullptr;
if (XNVCTRLIsNvScreen(display, screen) &&
XNVCTRLQueryStringAttribute(display, screen, 0,
NV_CTRL_STRING_NVIDIA_DRIVER_VERSION, &buffer))
{
ASSERT(buffer != nullptr);
*version = buffer;
XFree(buffer);
return true;
}
}
}
return false;
}
}
......@@ -70,4 +70,4 @@ bool IsBroxton(uint32_t DeviceId);
bool IsKabylake(uint32_t DeviceId);
} // namespace rx
#endif // LIBANGLE_RENDERER_DRIVER_UTILS_H_
\ No newline at end of file
#endif // LIBANGLE_RENDERER_DRIVER_UTILS_H_
......@@ -28,7 +28,6 @@ namespace
{
// Scan /etc/ati/amdpcsdb.default for "ReleaseVersion".
// Return empty string on failing.
egl::Error GetAMDDriverVersion(std::string *version)
{
*version = "";
......
......@@ -63,6 +63,24 @@
'image_util/loadimage.inl',
'image_util/loadimage_etc.cpp',
],
'libangle_gpu_info_util_sources':
[
'gpu_info_util/SystemInfo.cpp',
'gpu_info_util/SystemInfo.h',
'gpu_info_util/SystemInfo_internal.h',
],
'libangle_gpu_info_util_linux_sources':
[
'gpu_info_util/SystemInfo_linux.cpp',
],
'libangle_gpu_info_util_libpci_sources':
[
'gpu_info_util/SystemInfo_libpci.cpp',
],
'libangle_gpu_info_util_x11_sources':
[
'gpu_info_util/SystemInfo_x11.cpp',
],
'libangle_includes':
[
'../include/angle_gl.h',
......@@ -829,6 +847,7 @@
'commit_id',
'angle_common',
'angle_image_util',
'angle_gpu_info_util',
'libANGLE_d3d11_config',
'libANGLE_renderer_config',
],
......
......@@ -21,6 +21,7 @@
'<(angle_path)/src/common/string_utils_unittest.cpp',
'<(angle_path)/src/common/utilities_unittest.cpp',
'<(angle_path)/src/common/vector_utils_unittest.cpp',
'<(angle_path)/src/gpu_info_util/SystemInfo_unittest.cpp',
'<(angle_path)/src/libANGLE/BinaryStream_unittest.cpp',
'<(angle_path)/src/libANGLE/Config_unittest.cpp',
'<(angle_path)/src/libANGLE/Fence_unittest.cpp',
......
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