diff --git a/.github/actions/setup-dependencies/linux/action.yml b/.github/actions/setup-dependencies/linux/action.yml index fe7ee2142..fa5af702b 100644 --- a/.github/actions/setup-dependencies/linux/action.yml +++ b/.github/actions/setup-dependencies/linux/action.yml @@ -13,7 +13,7 @@ runs: dpkg-dev \ ninja-build extra-cmake-modules pkg-config scdoc \ cmark gamemode-dev libarchive-dev libcmark-dev libqrencode-dev zlib1g-dev \ - libxcb-cursor-dev libtomlplusplus-dev libvulkan-dev + libxcb-cursor-dev libtomlplusplus-dev - name: Setup AppImage tooling shell: bash diff --git a/Containerfile b/Containerfile index 8ee4330ed..59595fe55 100644 --- a/Containerfile +++ b/Containerfile @@ -28,7 +28,7 @@ RUN apt-get --assume-yes --no-install-recommends install \ # Build system cmake ninja-build extra-cmake-modules pkg-config \ # Dependencies - cmark gamemode-dev libarchive-dev libcmark-dev libgamemode0 libgl1-mesa-dev libqrencode-dev libtomlplusplus-dev libvulkan-dev scdoc zlib1g-dev \ + cmark gamemode-dev libarchive-dev libcmark-dev libgamemode0 libgl1-mesa-dev libqrencode-dev libtomlplusplus-dev scdoc zlib1g-dev \ # Tooling clang-format clang-tidy git diff --git a/launcher/HardwareInfo.cpp b/launcher/HardwareInfo.cpp index 3e20cc19b..425f07a09 100644 --- a/launcher/HardwareInfo.cpp +++ b/launcher/HardwareInfo.cpp @@ -18,84 +18,45 @@ #include "HardwareInfo.h" -#include -#include -#include -#include -#include "BuildConfig.h" - -#ifndef Q_OS_MACOS -#include -#include -#endif +#include +#include +#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX) namespace { -bool vulkanInfo(QStringList& out) +QString afterColon(QString str) { - if (!QProcessEnvironment::systemEnvironment() - .value(QStringLiteral("%1_DISABLE_GLVULKAN").arg(BuildConfig.LAUNCHER_ENVNAME)) - .isEmpty()) { - return false; - } -#ifndef Q_OS_MACOS - QVulkanInstance inst; - if (!inst.create()) { - qWarning() << "Vulkan instance creation failed, VkResult:" << inst.errorCode(); - out << "Couldn't get Vulkan device information"; - return false; - } - - QVulkanWindow window; - window.setVulkanInstance(&inst); - - for (auto device : window.availablePhysicalDevices()) { - const auto supportedVulkanVersion = QVersionNumber(VK_API_VERSION_MAJOR(device.apiVersion), VK_API_VERSION_MINOR(device.apiVersion), - VK_API_VERSION_PATCH(device.apiVersion)); - out << QString("Found Vulkan device: %1 (API version %2)").arg(device.deviceName).arg(supportedVulkanVersion.toString()); - } -#endif - - return true; + return str.remove(0, str.indexOf(':') + 2).trimmed(); } -bool openGlInfo(QStringList& out) +template +bool readFromOutput(const char* command, F function) { - if (!QProcessEnvironment::systemEnvironment() - .value(QStringLiteral("%1_DISABLE_GLVULKAN").arg(BuildConfig.LAUNCHER_ENVNAME)) - .isEmpty()) { - return false; - } - QOpenGLContext ctx; - if (!ctx.create()) { - qWarning() << "OpenGL context creation failed"; - out << "Couldn't get OpenGL device information"; + FILE* file = popen(command, "r"); // NOLINT(*-command-processor) + if (!file) { + qWarning().nospace() << "Could not execute command '" << command << "': " << strerror(errno); return false; } - QOffscreenSurface surface; - surface.create(); - ctx.makeCurrent(&surface); + constexpr size_t bufferSize = 512; + std::array buffer{}; + while (fgets(buffer.data(), bufferSize, file) != nullptr) { + function(buffer.data()); + } - auto* f = ctx.functions(); - f->initializeOpenGLFunctions(); + const int exitCode = pclose(file); + if (exitCode != 0) { + if (exitCode == -1) { + qWarning().nospace() << "Could not close stream for command '" << command << "': " << strerror(errno); + } else { + qWarning().nospace() << "Command '" << command << "' exited with code " << exitCode; + } - auto toQString = [](const GLubyte* str) { return QString(reinterpret_cast(str)); }; - out << "OpenGL driver vendor: " + toQString(f->glGetString(GL_VENDOR)); - out << "OpenGL renderer: " + toQString(f->glGetString(GL_RENDERER)); - out << "OpenGL driver version: " + toQString(f->glGetString(GL_VERSION)); + return false; + } return true; } } // namespace - -#ifndef Q_OS_LINUX -QStringList HardwareInfo::gpuInfo() -{ - QStringList info; - vulkanInfo(info); - openGlInfo(info); - return info; -} #endif #ifdef Q_OS_WINDOWS @@ -104,6 +65,7 @@ QStringList HardwareInfo::gpuInfo() #endif #include +#include "dxgi.h" #include "windows.h" QString HardwareInfo::cpuInfo() @@ -140,15 +102,43 @@ uint64_t HardwareInfo::availableRamMiB() return 0; } +QStringList HardwareInfo::gpuInfo() +{ + IDXGIFactory* factory = nullptr; + if (CreateDXGIFactory(__uuidof(IDXGIFactory), reinterpret_cast(&factory)) != S_OK) { // NOLINT(*-pro-type-reinterpret-cast) + qWarning() << "Could not create DXGI factory"; + return { "GPU discovery failed: could not create DXGI factory" }; + } + + UINT i = 0; + IDXGIAdapter* adapter = nullptr; + QStringList out; + + while (factory->EnumAdapters(i, &adapter) != DXGI_ERROR_NOT_FOUND) { + DXGI_ADAPTER_DESC desc; + if (adapter->GetDesc(&desc) != S_OK) { + qWarning() << "Could not get DXGI adapter description"; + } else { + out << "GPU: " + QString::fromWCharArray(desc.Description); // NOLINT(*-pro-bounds-array-to-pointer-decay, *-no-array-decay) + } + + adapter->Release(); + ++i; + } + + factory->Release(); + return out; +} + #elif defined(Q_OS_MACOS) #include "sys/sysctl.h" QString HardwareInfo::cpuInfo() { - std::array buffer; + std::array buffer{}; size_t bufferSize = buffer.size(); if (sysctlbyname("machdep.cpu.brand_string", &buffer, &bufferSize, nullptr, 0) == 0) { - return QString(buffer.data()); + return { buffer.data() }; } qWarning() << "Could not get CPU model: sysctlbyname"; @@ -157,7 +147,7 @@ QString HardwareInfo::cpuInfo() uint64_t HardwareInfo::totalRamMiB() { - uint64_t memsize; + uint64_t memsize = 0; size_t memsizeSize = sizeof memsize; if (sysctlbyname("hw.memsize", &memsize, &memsizeSize, nullptr, 0) == 0) { // transforming bytes -> mib @@ -175,7 +165,7 @@ uint64_t HardwareInfo::availableRamMiB() MacOSHardwareInfo::MemoryPressureLevel MacOSHardwareInfo::memoryPressureLevel() { - uint32_t level; + uint32_t level = 0; size_t levelSize = sizeof level; if (sysctlbyname("kern.memorystatus_vm_pressure_level", &level, &levelSize, nullptr, 0) == 0) { return static_cast(level); @@ -195,25 +185,37 @@ QString MacOSHardwareInfo::memoryPressureLevelName() return "Yellow"; case MemoryPressureLevel::Critical: return "Red"; + default: + Q_ASSERT(false); + return ""; } } +QStringList HardwareInfo::gpuInfo() +{ + QStringList out; + const bool success = readFromOutput("system_profiler SPDisplaysDataType", [&](const QString& str) { + // Chipset Model: Intel HD Graphics 620 + if (str.contains("Chipset Model")) { + out << "GPU: " + afterColon(str); + } + }); + if (!success) { + return { "GPU discovery failed: could not read from system_profiler" }; + } + + return out; +} + #elif defined(Q_OS_LINUX) #include -namespace { -QString afterColon(QString& str) -{ - return str.remove(0, str.indexOf(':') + 2).trimmed(); -} -} // namespace - QString HardwareInfo::cpuInfo() { std::ifstream cpuin("/proc/cpuinfo"); for (std::string line; std::getline(cpuin, line);) { // model name : AMD Ryzen 7 5800X 8-Core Processor - if (QString str = QString::fromStdString(line); str.startsWith("model name")) { + if (const QString str = QString::fromStdString(line); str.startsWith("model name")) { return afterColon(str); } } @@ -222,12 +224,13 @@ QString HardwareInfo::cpuInfo() return "unknown"; } -uint64_t readMemInfo(QString searchTarget) +namespace { +uint64_t readMemInfo(const QString& searchTarget) { std::ifstream memin("/proc/meminfo"); for (std::string line; std::getline(memin, line);) { // MemTotal: 16287480 kB - if (QString str = QString::fromStdString(line); str.startsWith(searchTarget)) { + if (const QString str = QString::fromStdString(line); str.startsWith(searchTarget)) { bool ok = false; const uint total = str.simplified().section(' ', 1, 1).toUInt(&ok); if (!ok) { @@ -243,6 +246,7 @@ uint64_t readMemInfo(QString searchTarget) qWarning() << "Could not read /proc/meminfo: search target not found:" << searchTarget; return 0; } +} // namespace uint64_t HardwareInfo::totalRamMiB() { @@ -256,52 +260,50 @@ uint64_t HardwareInfo::availableRamMiB() QStringList HardwareInfo::gpuInfo() { - QStringList list; - const bool vulkanSuccess = vulkanInfo(list); - const bool openGlSuccess = openGlInfo(list); - if (vulkanSuccess || openGlSuccess) { - return list; - } - - std::array buffer; - FILE* lspci = popen("lspci -k", "r"); - - if (!lspci) { - return { "Could not detect GPUs: lspci is not present" }; - } - bool readingGpuInfo = false; - QString currentModel = ""; - while (fgets(buffer.data(), 512, lspci) != nullptr) { - QString str(buffer.data()); + QString gpu; + QString driverInUse = "NONE"; + QString driversAvailable = "NONE"; + QStringList out; + + const bool success = readFromOutput("lspci -k", [&](const QString& str) { // clang-format off // 04:00.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (rev e7) // Subsystem: Sapphire Technology Limited Radeon RX 580 Pulse 4GB // Kernel driver in use: amdgpu // Kernel modules: amdgpu // clang-format on - if (str.contains("VGA compatible controller")) { + if (str.contains("VGA compatible controller") || str.contains("3D controller")) { readingGpuInfo = true; } else if (!str.startsWith('\t')) { + if (readingGpuInfo) { + out << QString("GPU: %1 (driver in use: %2; drivers available: %3)").arg(gpu, driverInUse, driversAvailable); + driverInUse = "NONE"; + driversAvailable = "NONE"; + } readingGpuInfo = false; } + if (!readingGpuInfo) { - continue; + return; } + const QString value = afterColon(str); if (str.contains("Subsystem")) { - currentModel = "Found GPU: " + afterColon(str); + gpu = value; } if (str.contains("Kernel driver in use")) { - currentModel += " (using driver " + afterColon(str); + driverInUse = value; } if (str.contains("Kernel modules")) { - currentModel += ", available drivers: " + afterColon(str) + ")"; - list.append(currentModel); + driversAvailable = value; } + }); + if (!success) { + return { "GPU discovery failed: could not read from lspci" }; } - pclose(lspci); - return list; + + return out; } #else @@ -316,19 +318,20 @@ QString HardwareInfo::cpuInfo() uint64_t HardwareInfo::totalRamMiB() { - char buff[512]; - FILE* fp = popen("sysctl hw.physmem", "r"); - if (fp != nullptr) { - if (fgets(buff, 512, fp) != nullptr) { - std::string str(buff); - uint64_t mem = std::stoull(str.substr(12, std::string::npos)); + uint64_t out = 0; - // transforming kib -> mib - return mem / 1024; - } + const bool success = readFromOutput("sysctl hw.physmem", [&](const QString& str) { + const uint64_t mem = str.mid(12).toULong(); + + // transforming kib -> mib + out = mem / 1024; + }); + if (!success) { + qWarning() << "Could not get total RAM: could not read from sysctl"; + return 0; } - return 0; + return out; } #else @@ -343,4 +346,8 @@ uint64_t HardwareInfo::availableRamMiB() return 0; } +QStringList HardwareInfo::gpuInfo() +{ + return { "GPU discovery failed: not implemented for this OS" }; +} #endif diff --git a/vcpkg.json b/vcpkg.json index 5fd336fff..942e6d9e4 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -26,7 +26,6 @@ ] }, "tomlplusplus", - "zlib", - "vulkan-headers" + "zlib" ] }