From 4cc3800384fd73a9f275426c114ba39992151f0a Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Fri, 5 Jul 2019 12:26:42 +0200 Subject: [PATCH] COmprehensive enumeration of gpu and displays from mac --- .../platform/src/platform/PlatformKeys.h | 10 + .../src/platform/backend/MACOSPlatform.cpp | 385 ++++++++++-------- .../src/platform/backend/Platform.cpp | 10 + .../src/platform/backend/WINPlatform.cpp | 37 +- 4 files changed, 248 insertions(+), 194 deletions(-) diff --git a/libraries/platform/src/platform/PlatformKeys.h b/libraries/platform/src/platform/PlatformKeys.h index ad9ae8e6e2..fceb3071a7 100644 --- a/libraries/platform/src/platform/PlatformKeys.h +++ b/libraries/platform/src/platform/PlatformKeys.h @@ -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; diff --git a/libraries/platform/src/platform/backend/MACOSPlatform.cpp b/libraries/platform/src/platform/backend/MACOSPlatform.cpp index 656a412268..76ad16f019 100644 --- a/libraries/platform/src/platform/backend/MACOSPlatform.cpp +++ b/libraries/platform/src/platform/backend/MACOSPlatform.cpp @@ -26,7 +26,7 @@ #include #include #include - +#include #include #endif @@ -45,6 +45,90 @@ void MACOSInstance::enumerateCpus() { void MACOSInstance::enumerateGpusAndDisplays() { #ifdef Q_OS_MAC + // ennumerate the displays first + std::vector displayMasks; + { + uint32_t numDisplays = 0; + CGError error = CGGetOnlineDisplayList(0, nullptr, &numDisplays); + if (numDisplays <= 0 || error != kCGErrorSuccess) { + return; + } + + std::vector 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 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 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 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()); - - // 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 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 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 } diff --git a/libraries/platform/src/platform/backend/Platform.cpp b/libraries/platform/src/platform/backend/Platform.cpp index 79687d8ba0..24ce099e0e 100644 --- a/libraries/platform/src/platform/backend/Platform.cpp +++ b/libraries/platform/src/platform/backend/Platform.cpp @@ -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"; diff --git a/libraries/platform/src/platform/backend/WINPlatform.cpp b/libraries/platform/src/platform/backend/WINPlatform.cpp index 2f0210de16..680861b550 100644 --- a/libraries/platform/src/platform/backend/WINPlatform.cpp +++ b/libraries/platform/src/platform/backend/WINPlatform.cpp @@ -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 -} \ No newline at end of file +}