COmprehensive enumeration of gpu and displays from mac

This commit is contained in:
Sam Gateau 2019-07-05 12:26:42 +02:00
parent 98f4448813
commit 4cc3800384
4 changed files with 248 additions and 194 deletions

View file

@ -44,6 +44,16 @@ namespace platform { namespace keys{
extern const char* boundsTop;
extern const char* boundsBottom;
extern const char* gpu;
extern const char* ppiHorizontal;
extern const char* ppiVertical;
extern const char* ppi;
extern const char* physicalWidth;
extern const char* physicalHeight;
extern const char* modeRefreshrate;
extern const char* modeWidth;
extern const char* modeHeight;
extern const char* desktopPpi;
extern const char* isMaster;
}
namespace memory {
extern const char* memTotal;

View file

@ -26,7 +26,7 @@
#include <CoreFoundation/CoreFoundation.h>
#include <ApplicationServices/ApplicationServices.h>
#include <QSysInfo>
#include <QString>
#include <OpenGL/OpenGL.h>
#endif
@ -45,6 +45,90 @@ void MACOSInstance::enumerateCpus() {
void MACOSInstance::enumerateGpusAndDisplays() {
#ifdef Q_OS_MAC
// ennumerate the displays first
std::vector<GLuint> displayMasks;
{
uint32_t numDisplays = 0;
CGError error = CGGetOnlineDisplayList(0, nullptr, &numDisplays);
if (numDisplays <= 0 || error != kCGErrorSuccess) {
return;
}
std::vector<CGDirectDisplayID> onlineDisplayIDs(numDisplays, 0);
error = CGGetOnlineDisplayList(onlineDisplayIDs.size(), onlineDisplayIDs.data(), &numDisplays);
if (error != kCGErrorSuccess) {
return;
}
for (auto displayID : onlineDisplayIDs) {
auto displaySize = CGDisplayScreenSize(displayID);
const auto MM_TO_IN = 0.0393701f;
auto displaySizeWidthInches = displaySize.width * MM_TO_IN;
auto displaySizeHeightInches = displaySize.height * MM_TO_IN;
auto displayBounds = CGDisplayBounds(displayID);
auto displayMaster =CGDisplayIsMain(displayID);
auto displayUnit =CGDisplayUnitNumber(displayID);
auto displayModel =CGDisplayModelNumber(displayID);
auto displayVendor = CGDisplayVendorNumber(displayID);
auto displaySerial = CGDisplaySerialNumber(displayID);
auto displayMode = CGDisplayCopyDisplayMode(displayID);
auto displayModeWidth = CGDisplayModeGetPixelWidth(displayMode);
auto displayModeHeight = CGDisplayModeGetPixelHeight(displayMode);
auto displayRefreshrate = CGDisplayModeGetRefreshRate(displayMode);
auto ppiH = displayModeWidth / displaySizeWidthInches;
auto ppiV = displayModeHeight / displaySizeHeightInches;
auto ppiHScaled = displayBounds.size.width / displaySizeWidthInches;
auto ppiVScaled = displayBounds.size.height / displaySizeHeightInches;
auto glmask = CGDisplayIDToOpenGLDisplayMask(displayID);
// Metal device ID is the recommended new way but requires objective c
// auto displayDevice = CGDirectDisplayCopyCurrentMetalDevice(displayID);
CGDisplayModeRelease(displayMode);
json display = {};
// Rect region of the desktop in desktop units
display[keys::display::boundsLeft] = displayBounds.origin.x;
display[keys::display::boundsRight] = displayBounds.origin.x + displayBounds.size.width;
display[keys::display::boundsTop] = displayBounds.origin.y;
display[keys::display::boundsBottom] = displayBounds.origin.y + displayBounds.size.height;
// PPI & resolution
display[keys::display::ppiHorizontal] = ppiH;
display[keys::display::ppiVertical] = ppiV;
display[keys::display::physicalWidth] = displaySizeWidthInches;
display[keys::display::physicalHeight] = displaySizeHeightInches;
display[keys::display::modeWidth] = displayModeWidth;
display[keys::display::modeHeight] = displayModeHeight;
//Average the ppiH and V for the simple ppi
display[keys::display::ppi] = std::round(0.5f * (ppiH + ppiV));
display[keys::display::desktopPpi] = std::round(0.5f * (ppiHScaled + ppiVScaled));
// refreshrate
display[keys::display::modeRefreshrate] = displayRefreshrate;
// Master display ?
display[keys::display::isMaster] = displayMaster;
// Macos specific
display["macos_unit"] = displayUnit;
display["macos_vendor"] = displayVendor;
display["macos_model"] = displayModel;
display["macos_serial"] = displaySerial;
display["macos_glmask"] = glmask;
displayMasks.push_back(glmask);
_displays.push_back(display);
}
}
// Collect Renderer info as exposed by the CGL layers
GLuint cglDisplayMask = -1; // Iterate over all of them.
CGLRendererInfoObj rendererInfo;
@ -54,210 +138,155 @@ void MACOSInstance::enumerateGpusAndDisplays() {
return;
}
struct CGLRendererDesc {
int rendererID{0};
int deviceVRAM{0};
int majorGLVersion{0};
int registryIDLow{0};
int registryIDHigh{0};
int displayMask{0};
};
std::vector<CGLRendererDesc> renderers(rendererInfoCount);
// Iterate over all of the renderers and use the figure for the one with the most VRAM,
// on the assumption that this is the one that will actually be used.
for (GLint i = 0; i < rendererInfoCount; i++) {
auto& desc = renderers[i];
struct CGLRendererDesc {
int rendererID{0};
int deviceVRAM{0};
int accelerated{0};
int displayMask{0};
} desc;
CGLDescribeRenderer(rendererInfo, i, kCGLRPRendererID, &desc.rendererID);
CGLDescribeRenderer(rendererInfo, i, kCGLRPVideoMemoryMegabytes, &desc.deviceVRAM);
CGLDescribeRenderer(rendererInfo, i, kCGLRPMajorGLVersion, &desc.majorGLVersion);
CGLDescribeRenderer(rendererInfo, i, kCGLRPRegistryIDLow, &desc.registryIDLow);
CGLDescribeRenderer(rendererInfo, i, kCGLRPRegistryIDHigh, &desc.registryIDHigh);
CGLDescribeRenderer(rendererInfo, i, kCGLRPDisplayMask, &desc.displayMask);
CGLDescribeRenderer(rendererInfo, i, kCGLRPAccelerated, &desc.accelerated);
kCGLRPDisplayMask
// If this renderer is not hw accelerated then just skip it in the enumeration
if (!desc.accelerated) {
continue;
}
// From the rendererID identify the vendorID
// https://github.com/apitrace/apitrace/blob/master/retrace/glws_cocoa.mm
GLint vendorID = desc.rendererID & kCGLRendererIDMatchingMask & ~0xfff;
const GLint VENDOR_ID_SOFTWARE { 0x00020000 };
const GLint VENDOR_ID_AMD { 0x00021000 };
const GLint VENDOR_ID_NVIDIA { 0x00022000 };
const GLint VENDOR_ID_INTEL { 0x00024000 };
const char* vendorName;
switch (vendorID) {
case VENDOR_ID_SOFTWARE:
// Software renderer then skip it (should already have been caught by hwaccelerated test abve
continue;
break;
case VENDOR_ID_AMD:
vendorName = keys::gpu::vendor_AMD;
break;
case VENDOR_ID_NVIDIA:
vendorName = keys::gpu::vendor_NVIDIA;
break;
case VENDOR_ID_INTEL:
vendorName = keys::gpu::vendor_Intel;
break;
default:
vendorName = keys::UNKNOWN;
break;
}
//once we reach this point, the renderer is legit
// Start defining the gpu json
json gpu = {};
gpu["A"] =desc.rendererID;
gpu["B"] =desc.deviceVRAM;
gpu["C"] =desc.majorGLVersion;
gpu[keys::gpu::vendor] = vendorName;
gpu[keys::gpu::videoMemory] = desc.deviceVRAM;
gpu["macos_rendererID"] = desc.rendererID;
gpu["macos_displayMask"] = desc.displayMask;
gpu["D"] =desc.registryIDLow;
gpu["E"] =desc.registryIDHigh;
gpu["F"] =desc.displayMask;
std::vector<int> displayIndices;
for (int d = 0; d < (int) displayMasks.size(); d++) {
if (desc.displayMask & displayMasks[d]) {
displayIndices.push_back(d);
_displays[d][keys::display::gpu] = _gpus.size();
}
}
gpu[keys::gpu::displays] = displayIndices;
_gpus.push_back(gpu);
}
CGLDestroyRendererInfo(rendererInfo);
//get gpu / display information from the system profiler
FILE* stream = popen("system_profiler SPDisplaysDataType | grep -e Chipset -e VRAM -e Vendor -e \"Device ID\" -e Displays -e \"Display Type\" -e Resolution -e \"Main Display\"", "r");
std::ostringstream hostStream;
while (!feof(stream) && !ferror(stream)) {
char buf[128];
int bytesRead = fread(buf, 1, 128, stream);
hostStream.write(buf, bytesRead);
}
std::string gpuArrayDesc = hostStream.str();
{ //get gpu information from the system profiler that we don't know how to retreive otherwise
struct ChipsetModelDesc {
std::string model;
std::string vendor;
int deviceID{0};
int videoMemory{0};
};
std::vector<ChipsetModelDesc> chipsetDescs;
FILE* stream = popen("system_profiler SPDisplaysDataType | grep -e Chipset -e VRAM -e Vendor -e \"Device ID\"", "r");
std::ostringstream hostStream;
while (!feof(stream) && !ferror(stream)) {
char buf[128];
int bytesRead = fread(buf, 1, 128, stream);
hostStream.write(buf, bytesRead);
}
std::string gpuArrayDesc = hostStream.str();
// Find the Chipset model first
const std::regex chipsetModelToken("(Chipset Model: )(.*)");
std::smatch found;
// Find the Chipset model first
const std::regex chipsetModelToken("(Chipset Model: )(.*)");
std::smatch found;
while (std::regex_search(gpuArrayDesc, found, chipsetModelToken)) {
json gpu = {};
gpu[keys::gpu::model] = found.str(2);
// Find the end of the gpu block
gpuArrayDesc = found.suffix();
std::string gpuDesc = gpuArrayDesc;
const std::regex endGpuToken("Chipset Model: ");
if (std::regex_search(gpuArrayDesc, found, endGpuToken)) {
gpuDesc = found.prefix();
}
// Find the vendor
gpu[keys::gpu::vendor] = findGPUVendorInDescription(gpu[keys::gpu::model].get<std::string>());
// Find the memory amount in MB
const std::regex memoryToken("(VRAM .*: )(.*)");
if (std::regex_search(gpuDesc, found, memoryToken)) {
auto memAmountUnit = found.str(2);
std::smatch amount;
const std::regex memAmountGBToken("(\\d*) GB");
const std::regex memAmountMBToken("(\\d*) MB");
const int GB_TO_MB { 1024 };
if (std::regex_search(memAmountUnit, amount, memAmountGBToken)) {
gpu[keys::gpu::videoMemory] = std::stoi(amount.str(1)) * GB_TO_MB;
} else if (std::regex_search(memAmountUnit, amount, memAmountMBToken)) {
gpu[keys::gpu::videoMemory] = std::stoi(amount.str(1));
} else {
gpu[keys::gpu::videoMemory] = found.str(2);
}
}
// Find the Device ID
const std::regex deviceIDToken("(Device ID: )(.*)");
if (std::regex_search(gpuDesc, found, deviceIDToken)) {
gpu["deviceID"] = std::stoi(found.str(2));
}
// Enumerate the Displays
const std::regex displaysToken("(Displays: )");
if (std::regex_search(gpuDesc, found, displaysToken)) {
std::string displayArrayDesc = found.suffix();
std::vector<json> displays;
while (std::regex_search(gpuArrayDesc, found, chipsetModelToken)) {
ChipsetModelDesc desc;
desc.model = found.str(2);
int displayIndex = 0;
const std::regex displayTypeToken("(Display Type: )(.*)");
while (std::regex_search(displayArrayDesc, found, displayTypeToken)) {
json display = {};
display["display"] = found.str(2);
// Find the end of the display block
displayArrayDesc = found.suffix();
std::string displayDesc = displayArrayDesc;
const std::regex endDisplayToken("Display Type: ");
if (std::regex_search(displayArrayDesc, found, endDisplayToken)) {
displayDesc = found.prefix();
}
// Find the resolution
const std::regex resolutionToken("(Resolution: )(.*)");
if (std::regex_search(displayDesc, found, deviceIDToken)) {
display["resolution"] = found.str(2);
}
// Find is main display
const std::regex mainMonitorToken("(Main Display: )(.*)");
if (std::regex_search(displayDesc, found, mainMonitorToken)) {
display["isMaster"] = found.str(2);
// Find the end of the gpu block
gpuArrayDesc = found.suffix();
std::string gpuDesc = gpuArrayDesc;
const std::regex endGpuToken("Chipset Model: ");
if (std::regex_search(gpuArrayDesc, found, endGpuToken)) {
gpuDesc = found.prefix();
}
// Find the vendor
desc.vendor = findGPUVendorInDescription(desc.model);
// Find the memory amount in MB
const std::regex memoryToken("(VRAM .*: )(.*)");
if (std::regex_search(gpuDesc, found, memoryToken)) {
auto memAmountUnit = found.str(2);
std::smatch amount;
const std::regex memAmountGBToken("(\\d*) GB");
const std::regex memAmountMBToken("(\\d*) MB");
const int GB_TO_MB { 1024 };
if (std::regex_search(memAmountUnit, amount, memAmountGBToken)) {
desc.videoMemory = std::stoi(amount.str(1)) * GB_TO_MB;
} else if (std::regex_search(memAmountUnit, amount, memAmountMBToken)) {
desc.videoMemory = std::stoi(amount.str(1));
} else {
display["isMaster"] = "false";
desc.videoMemory = std::stoi(memAmountUnit);
}
display["index"] = displayIndex;
displayIndex++;
displays.push_back(display);
}
if (!displays.empty()) {
gpu["displays"] = displays;
// Find the Device ID
const std::regex deviceIDToken("(Device ID: )(.*)");
if (std::regex_search(gpuDesc, found, deviceIDToken)) {
desc.deviceID = std::stoi(found.str(2), 0, 16);
}
chipsetDescs.push_back(desc);
}
// GO through the detected gpus in order and complete missing information from ChipsetModelDescs
// assuming the order is the same and checking that the vendor and memory amount match as a simple check
auto numDescs = (int) std::min(chipsetDescs.size(),_gpus.size());
for (int i = 0; i < numDescs; ++i) {
const auto& chipset = chipsetDescs[i];
auto& gpu = _gpus[i];
if ( (chipset.vendor.find( gpu[keys::gpu::vendor]) != std::string::npos)
&& (chipset.videoMemory == gpu[keys::gpu::videoMemory]) ) {
gpu[keys::gpu::model] = chipset.model;
gpu["macos_deviceID"] = chipset.deviceID;
}
}
_gpus.push_back(gpu);
}
#endif
#ifdef Q_OS_MAC
uint32_t numDisplays = 0;
CGError error = CGGetOnlineDisplayList(0, nullptr, &numDisplays);
if (numDisplays <= 0 || error != kCGErrorSuccess) {
return;
}
std::vector<CGDirectDisplayID> onlineDisplayIDs(numDisplays, 0);
error = CGGetOnlineDisplayList(onlineDisplayIDs.size(), onlineDisplayIDs.data(), &numDisplays);
if (error != kCGErrorSuccess) {
return;
}
for (auto displayID : onlineDisplayIDs) {
auto displaySize = CGDisplayScreenSize(displayID);
auto glmask = CGDisplayIDToOpenGLDisplayMask(displayID);
const auto MM_TO_IN = 0.0393701;
auto displaySizeWidthInches = displaySize.width * MM_TO_IN;
auto displaySizeHeightInches = displaySize.height * MM_TO_IN;
auto displaySizeDiagonalInches = sqrt(displaySizeWidthInches * displaySizeWidthInches + displaySizeHeightInches * displaySizeHeightInches);
auto displayBounds = CGDisplayBounds(displayID);
auto displayMaster =CGDisplayIsMain(displayID);
auto displayUnit =CGDisplayUnitNumber(displayID);
auto displayModel =CGDisplayModelNumber(displayID);
auto displayVendor = CGDisplayVendorNumber(displayID);
auto displaySerial = CGDisplaySerialNumber(displayID);
auto displayMode = CGDisplayCopyDisplayMode(displayID);
auto displayModeWidth = CGDisplayModeGetPixelWidth(displayMode);
auto displayModeHeight = CGDisplayModeGetPixelHeight(displayMode);
auto displayRefreshrate = CGDisplayModeGetRefreshRate(displayMode);
CGDisplayModeRelease(displayMode);
json display = {};
display["physicalWidth"] = displaySizeWidthInches;
display["physicalHeight"] = displaySizeHeightInches;
display["physicalDiagonal"] = displaySizeDiagonalInches;
display["ppi"] = sqrt(displayModeHeight * displayModeHeight + displayModeWidth * displayModeWidth) / displaySizeDiagonalInches;
display[keys::display::boundsLeft] = displayBounds.origin.x;
display[keys::display::boundsRight] = displayBounds.origin.x + displayBounds.size.width;
display[keys::display::boundsTop] = displayBounds.origin.y;
display[keys::display::boundsBottom] = displayBounds.origin.y + displayBounds.size.height;
display["isMaster"] = displayMaster;
display["unit"] = displayUnit;
display["vendor"] = displayVendor;
display["model"] = displayModel;
display["serial"] = displaySerial;
display["refreshrate"] =displayRefreshrate;
display["modeWidth"] = displayModeWidth;
display["modeHeight"] = displayModeHeight;
display["glmask"] = glmask;
_displays.push_back(display);
}
#endif
}

View file

@ -45,6 +45,16 @@ namespace platform { namespace keys {
const char* boundsTop = "boundsTop";
const char* boundsBottom = "boundsBottom";
const char* gpu = "gpu";
const char* ppiHorizontal = "ppiHorizontal";
const char* ppiVertical = "ppiHorizontal";
const char* ppi = "ppi";
const char* physicalWidth = "physicalWidth";
const char* physicalHeight = "physicalHeight";
const char* modeRefreshrate = "modeRefreshrate";
const char* modeWidth = "modeWidth";
const char* modeHeight = "modeHeight";
const char* desktopPpi = "desktopPpi";
const char* isMaster = "isMaster";
}
namespace memory {
const char* memTotal = "memTotal";

View file

@ -127,27 +127,30 @@ void WINInstance::enumerateGpusAndDisplays() {
display[keys::display::description] = "";
// Rect region of the desktop in desktop units
display["desktopRect"] = (outputDesc.AttachedToDesktop ? true : false);
//display["desktopRect"] = (outputDesc.AttachedToDesktop ? true : false);
display[keys::display::boundsLeft] = outputDesc.DesktopCoordinates.left;
display[keys::display::boundsRight] = outputDesc.DesktopCoordinates.right;
display[keys::display::boundsBottom] = outputDesc.DesktopCoordinates.bottom;
display[keys::display::boundsTop] = outputDesc.DesktopCoordinates.top;
// DPI
display["ppiX"] = dpiX;
display["ppiY"] = dpiY;
display["physicalWidth"] = devMode.dmPelsWidth / (float) dpiX;
display["physicalHeight"] = devMode.dmPelsHeight / (float) dpiY;
display["modeWidth"] = devMode.dmPelsWidth;
display["modeHeight"] = devMode.dmPelsHeight;
//Average the ppiX and Y scaled vs the the true ppi
display["ppi"] = 0.5f * (dpiX + dpiY);
display["desktopPPIScale"] = 0.5f * (dpiX / (float) dpiXScaled + dpiY / (float)dpiYScaled);
// PPI & resolution
display[keys::display::ppiHorizontal] = dpiX;
display[keys::display::ppiVertical] = dpiY;
display[keys::display::physicalWidth] = devMode.dmPelsWidth / (float) dpiX;
display[keys::display::physicalHeight] = devMode.dmPelsHeight / (float) dpiY;
display[keys::display::modeWidth] = devMode.dmPelsWidth;
display[keys::display::modeHeight] = devMode.dmPelsHeight;
//Average the ppiH and V for the simple ppi
display[keys::display::ppi] = std::round(0.5f * (dpiX + dpiY));
display[keys::display::desktopPpi] = std::round(0.5f * (dpiXScaled + dpiYScaled));
// refreshrate
display["frequency"] = devMode.dmDisplayFrequency;
display[keys::display::modeRefreshrate] = devMode.dmDisplayFrequency;;
// Master display ?
display[keys::display::isMaster] = true;
// Add the display index to the list of displays of the gpu
displayIndices.push_back(_displays.size());
@ -188,11 +191,13 @@ void WINInstance::enumerateComputer() {
_computer[keys::computer::vendor] = "";
_computer[keys::computer::model] = "";
#ifdef Q_OS_WIN
auto sysInfo = QSysInfo();
_computer[keys::computer::OSVersion] = sysInfo.kernelVersion().toStdString();
#endif
}
void WINInstance::enumerateNics() {
// Start with the default from QT, getting the result into _nics:
Instance::enumerateNics();
@ -231,4 +236,4 @@ void WINInstance::enumerateNics() {
free(pAdapterInfo);
}
#endif
}
}