Commit d83f64f5 by Corentin Wallez Committed by Commit Bot

gpu_info_util: Implement GetSystemInfo on OSX

Also adds a test that prints the gathered information for manual checking and to help know what the system is when looking at the logs. BUG=angleproject:1874 Change-Id: Icb0cc390c9808fd8db0f966d667b94dde4b94e62 Reviewed-on: https://chromium-review.googlesource.com/443845 Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarZhenyao Mo <zmo@chromium.org>
parent 0a17c92c
......@@ -235,6 +235,15 @@ static_library("angle_gpu_info_util") {
defines += [ "GPU_INFO_USE_LIBPCI" ]
libs += [ "pci" ]
}
if (is_mac) {
sources +=
rebase_path(gles_gypi.libangle_gpu_info_util_mac_sources, ".", "src")
libs += [
"IOKit.framework",
"CoreFoundation.framework",
]
}
}
static_library("translator") {
......
......@@ -151,6 +151,14 @@
[
'<@(libangle_common_mac_sources)',
],
'link_settings':
{
'libraries':
[
'$(SDKROOT)/System/Library/Frameworks/IOKit.framework',
'$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework',
],
},
}],
['OS=="linux"',
{
......@@ -271,6 +279,13 @@
],
},
}],
['OS=="mac"',
{
'sources':
[
'<@(libangle_gpu_info_util_mac_sources)',
],
}],
],
},
......
......@@ -11,6 +11,8 @@
#include <cstring>
#include <sstream>
#include "common/debug.h"
namespace angle
{
......@@ -75,4 +77,69 @@ bool ParseAMDCatalystDriverVersion(const std::string &content, std::string *vers
return false;
}
bool ParseMacMachineModel(const std::string &identifier,
std::string *type,
int32_t *major,
int32_t *minor)
{
size_t numberLoc = identifier.find_first_of("0123456789");
if (numberLoc == std::string::npos)
{
return false;
}
size_t commaLoc = identifier.find(',', numberLoc);
if (commaLoc == std::string::npos || commaLoc >= identifier.size())
{
return false;
}
const char *numberPtr = &identifier[numberLoc];
const char *commaPtr = &identifier[commaLoc + 1];
char *endPtr = nullptr;
int32_t majorTmp = std::strtol(numberPtr, &endPtr, 10);
if (endPtr == numberPtr)
{
return false;
}
int32_t minorTmp = std::strtol(commaPtr, &endPtr, 10);
if (endPtr == commaPtr)
{
return false;
}
*major = majorTmp;
*minor = minorTmp;
*type = identifier.substr(0, numberLoc);
return true;
}
void FindPrimaryGPU(SystemInfo *info)
{
ASSERT(!info->gpus.empty());
// 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 = static_cast<int>(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);
}
} // namespace angle
......@@ -26,8 +26,8 @@ constexpr VendorID kVendorID_Qualcomm = 0x5143;
struct GPUDeviceInfo
{
VendorID vendorId;
DeviceID deviceId;
VendorID vendorId = 0;
DeviceID deviceId = 0;
std::string driverVendor;
std::string driverVersion;
......@@ -37,12 +37,14 @@ struct GPUDeviceInfo
struct SystemInfo
{
std::vector<GPUDeviceInfo> gpus;
int primaryGPUIndex;
int primaryGPUIndex = -1;
int activeGPUIndex = -1;
bool isOptimus;
bool isAMDSwitchable;
bool isOptimus = false;
bool isAMDSwitchable = false;
std::string machineModelName;
std::string machineModelVersion;
};
bool GetSystemInfo(SystemInfo *info);
......
......@@ -23,6 +23,14 @@ bool GetNvidiaDriverVersionWithXNVCtrl(std::string *version);
// Live in SystemInfo.cpp
bool ParseAMDBrahmaDriverVersion(const std::string &content, std::string *version);
bool ParseAMDCatalystDriverVersion(const std::string &content, std::string *version);
bool ParseMacMachineModel(const std::string &identifier,
std::string *type,
int32_t *major,
int32_t *minor);
// In the case there are multiple GPUs, this finds the primary one and sets Optimus or AMD
// Switchable
void FindPrimaryGPU(SystemInfo *info);
} // namespace angle
......
......@@ -81,25 +81,7 @@ bool GetSystemInfo(SystemInfo *info)
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);
FindPrimaryGPU(info);
for (size_t i = 0; i < info->gpus.size(); ++i)
{
......
//
// Copyright (c) 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_mac.cpp: implementation of the Mac-specific parts of SystemInfo.h
#include "gpu_info_util/SystemInfo_internal.h"
#import <Cocoa/Cocoa.h>
#import <IOKit/IOKitLib.h>
namespace angle
{
namespace
{
std::string GetMachineModel()
{
io_service_t platformExpert = IOServiceGetMatchingService(
kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
if (platformExpert == IO_OBJECT_NULL)
{
return "";
}
CFDataRef modelData = static_cast<CFDataRef>(
IORegistryEntryCreateCFProperty(platformExpert, CFSTR("model"), kCFAllocatorDefault, 0));
if (modelData == nullptr)
{
IOObjectRelease(platformExpert);
return "";
}
std::string result = reinterpret_cast<const char *>(CFDataGetBytePtr(modelData));
IOObjectRelease(platformExpert);
CFRelease(modelData);
return result;
}
// Extracts one integer property from a registry entry.
bool GetEntryProperty(io_registry_entry_t entry, CFStringRef name, uint32_t *value)
{
*value = 0;
CFDataRef data = static_cast<CFDataRef>(
IORegistryEntrySearchCFProperty(entry, kIOServicePlane, name, kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents));
if (data == nullptr)
{
return false;
}
const uint32_t *valuePtr = reinterpret_cast<const uint32_t *>(CFDataGetBytePtr(data));
if (valuePtr == nullptr)
{
CFRelease(data);
return false;
}
*value = *valuePtr;
CFRelease(data);
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)
{
// matchDictionary will be consumed by IOServiceGetMatchingServices, no need to release it.
CFMutableDictionaryRef matchDictionary = IOServiceMatching("IOPCIDevice");
io_iterator_t entryIterator;
if (IOServiceGetMatchingServices(kIOMasterPortDefault, matchDictionary, &entryIterator) !=
kIOReturnSuccess)
{
return false;
}
io_registry_entry_t entry = IO_OBJECT_NULL;
while ((entry = IOIteratorNext(entryIterator)) != IO_OBJECT_NULL)
{
constexpr uint32_t kClassCodeDisplayVGA = 0x30000;
uint32_t classCode;
GPUDeviceInfo info;
if (GetEntryProperty(entry, CFSTR("class-code"), &classCode) &&
classCode == kClassCodeDisplayVGA &&
GetEntryProperty(entry, CFSTR("vendor-id"), &info.vendorId) &&
GetEntryProperty(entry, CFSTR("device-id"), &info.deviceId))
{
devices->push_back(info);
}
IOObjectRelease(entry);
}
IOObjectRelease(entryIterator);
return true;
}
} // anonymous namespace
bool GetSystemInfo(SystemInfo *info)
{
{
int32_t major = 0;
int32_t minor = 0;
ParseMacMachineModel(GetMachineModel(), &info->machineModelName, &major, &minor);
info->machineModelVersion = std::to_string(major) + "." + std::to_string(minor);
}
if (!GetPCIDevices(&(info->gpus)))
{
return false;
}
if (info->gpus.empty())
{
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);
return true;
}
} // namespace angle
......@@ -71,4 +71,33 @@ TEST(SystemInfoTest, AMDCatalystVersionParsing)
ASSERT_TRUE(ParseAMDCatalystDriverVersion("ReleaseVersion=42.0.56 is the version", &version));
ASSERT_EQ("42.0.56", version);
}
// Test Mac machine model parsing
TEST(SystemInfoTest, MacMachineModelParsing)
{
std::string model;
int32_t major = 1, minor = 2;
// Test on the empty string, that is returned by GetMachineModel on an error
EXPECT_FALSE(ParseMacMachineModel("", &model, &major, &minor));
EXPECT_EQ(0U, model.length());
EXPECT_EQ(1, major);
EXPECT_EQ(2, minor);
// Test on an invalid string
EXPECT_FALSE(ParseMacMachineModel("FooBar", &model, &major, &minor));
// Test on a MacPro model
EXPECT_TRUE(ParseMacMachineModel("MacPro4,1", &model, &major, &minor));
EXPECT_EQ("MacPro", model);
EXPECT_EQ(4, major);
EXPECT_EQ(1, minor);
// Test on a MacBookPro model
EXPECT_TRUE(ParseMacMachineModel("MacBookPro6,2", &model, &major, &minor));
EXPECT_EQ("MacBookPro", model);
EXPECT_EQ(6, major);
EXPECT_EQ(2, minor);
}
} // anonymous namespace
......@@ -81,6 +81,10 @@
[
'gpu_info_util/SystemInfo_x11.cpp',
],
'libangle_gpu_info_util_mac_sources':
[
'gpu_info_util/SystemInfo_mac.mm',
],
'libangle_includes':
[
'../include/angle_gl.h',
......
......@@ -15,6 +15,7 @@
{
'angle_white_box_tests_sources':
[
'<(angle_path)/src/tests/util_tests/PrintSystemInfoTest.cpp',
'<(angle_path)/src/tests/test_utils/angle_test_configs.cpp',
'<(angle_path)/src/tests/test_utils/angle_test_configs.h',
'<(angle_path)/src/tests/test_utils/angle_test_instantiate.cpp',
......
//
// 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.
//
// PrintSystemInfoTest.cpp:
// prints the information gathered about the system so that it appears in the test logs
#include <gtest/gtest.h>
#include <iostream>
#include "common/platform.h"
#include "gpu_info_util/SystemInfo.h"
using namespace angle;
namespace
{
#if defined(ANGLE_PLATFORM_LINUX) || defined(ANGLE_PLATFORM_APPLE)
#define SYSTEM_INFO_IMPLEMENTED
#endif
#if defined(SYSTEM_INFO_IMPLEMENTED)
std::string VendorName(VendorID vendor)
{
switch (vendor)
{
case kVendorID_AMD:
return "AMD";
case kVendorID_Intel:
return "Intel";
case kVendorID_Nvidia:
return "Nvidia";
case kVendorID_Qualcomm:
return "Qualcomm";
default:
return "Unknown (" + std::to_string(vendor) + ")";
}
}
#endif
// Prints the information gathered about the system
TEST(PrintSystemInfoTest, Print)
{
#if defined(SYSTEM_INFO_IMPLEMENTED)
SystemInfo info;
ASSERT_TRUE(GetSystemInfo(&info));
ASSERT_GT(info.gpus.size(), 0u);
std::cout << info.gpus.size() << " GPUs:\n";
for (size_t i = 0; i < info.gpus.size(); i++)
{
const auto &gpu = info.gpus[i];
std::cout << " " << i << " - " << VendorName(gpu.vendorId) << " device " << gpu.deviceId
<< "\n";
if (!gpu.driverVendor.empty())
{
std::cout << " Driver Vendor: " << gpu.driverVendor << "\n";
}
if (!gpu.driverVersion.empty())
{
std::cout << " Driver Version: " << gpu.driverVersion << "\n";
}
if (!gpu.driverDate.empty())
{
std::cout << " Driver Date: " << gpu.driverDate << "\n";
}
}
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";
std::cout << "AMD Switchable: " << (info.isAMDSwitchable ? "true" : "false") << "\n";
std::cout << "\n";
if (!info.machineModelName.empty() || !info.machineModelVersion.empty())
{
std::cout << "Machine Model: " << info.machineModelName << " version "
<< info.machineModelVersion << "\n";
}
std::cout << std::endl;
#else
std::cerr << "GetSystemInfo not implemented, skipping" << std::endl;
#endif
}
} // anonymous namespace
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