diff --git a/README.md b/README.md
index d6197b6689..742b99d639 100644
--- a/README.md
+++ b/README.md
@@ -644,8 +644,8 @@ Example output:
Total VRAM |
🟢 |
🟢 |
- 🔴 |
- 🔴 |
+ 🟢 |
+ 🟢 |
🔴 |
🔴 |
🔴 |
diff --git a/mangohud-next/legacy_gpu_wrapper/meson.build b/mangohud-next/legacy_gpu_wrapper/meson.build
new file mode 100644
index 0000000000..9adec67be0
--- /dev/null
+++ b/mangohud-next/legacy_gpu_wrapper/meson.build
@@ -0,0 +1,28 @@
+mangohud_legacy_gpu_wrapper = static_library(
+ 'MangoHud_LegacyGPUWrapper',
+ files(
+ 'wrapper.cpp',
+ '../server/common/helpers.cpp',
+ '../server/metrics/fdinfo.cpp',
+ '../server/metrics/hwmon.cpp',
+ '../server/metrics/gpu/gpu.cpp',
+ '../server/metrics/gpu/intel/i915/i915.cpp',
+ '../server/metrics/gpu/intel/i915/i915_drm.cpp',
+ '../server/metrics/gpu/intel/xe/xe.cpp',
+ '../server/metrics/gpu/intel/xe/xe_drm.cpp',
+ '../server/metrics/gpu/amdgpu/amdgpu.cpp',
+ '../server/metrics/gpu/amdgpu/gpu_metrics.cpp',
+ '../server/metrics/gpu/nvidia/nvidia.cpp',
+ '../server/metrics/gpu/nvidia/nvml_loader.cpp',
+ '../server/metrics/gpu/msm/dpu.cpp',
+ '../server/metrics/gpu/msm/kgsl.cpp',
+ '../server/metrics/gpu/panfrost.cpp',
+ '../server/metrics/gpu/panthor.cpp'
+ ),
+ cpp_args: '-DMANGOHUD_LEGACY -DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE',
+ include_directories: [
+ '../server/metrics',
+ '../server/metrics/gpu'
+ ],
+ dependencies: [spdlog_dep, libcap_dep, libdrm_dep]
+)
diff --git a/mangohud-next/legacy_gpu_wrapper/wrapper.cpp b/mangohud-next/legacy_gpu_wrapper/wrapper.cpp
new file mode 100644
index 0000000000..90e4af4d37
--- /dev/null
+++ b/mangohud-next/legacy_gpu_wrapper/wrapper.cpp
@@ -0,0 +1,128 @@
+#include "gpu/intel/i915/i915.hpp"
+#include "gpu/intel/xe/xe.hpp"
+#include "gpu/msm/dpu.hpp"
+#include "gpu/msm/kgsl.hpp"
+#include "gpu/panfrost.hpp"
+#include "gpu/panthor.hpp"
+
+#include "wrapper.hpp"
+
+struct LegacyGPUWrapper::Impl {
+ Impl(
+ const std::string& driver, const std::string& drm_node, const std::string& pci_dev,
+ uint16_t vendor_id, uint16_t device_id
+ ) {
+ if (driver == "i915") {
+ gpu = std::make_unique(drm_node, pci_dev, vendor_id, device_id);
+ } else if (driver == "xe") {
+ gpu = std::make_unique(drm_node, pci_dev, vendor_id, device_id);
+ } else if (driver == "msm_dpu") {
+ gpu = std::make_unique(drm_node, pci_dev, vendor_id, device_id);
+ } else if (driver == "msm_drm") {
+ gpu = std::make_unique(drm_node, pci_dev, vendor_id, device_id);
+ } else if (driver == "panfrost") {
+ gpu = std::make_unique(drm_node, pci_dev, vendor_id, device_id);
+ } else if (driver == "panthor") {
+ gpu = std::make_unique(drm_node, pci_dev, vendor_id, device_id);
+ }
+
+ if (gpu) {
+ gpu->start_thread_worker();
+ } else {
+ SPDLOG_WARN("Failed to construct gpu with driver {} on node {}", driver, drm_node);
+ }
+ }
+
+ ~Impl() {}
+
+ std::unique_ptr gpu;
+
+ std::map process_metrics;
+ gpu_metrics_system_t system_metrics;
+};
+
+LegacyGPUWrapper::LegacyGPUWrapper(
+ const std::string& driver, const std::string& drm_node, const std::string& pci_dev,
+ uint16_t vendor_id, uint16_t device_id
+) {
+ m_impl = std::make_unique(driver, drm_node, pci_dev, vendor_id, device_id);
+}
+
+LegacyGPUWrapper::~LegacyGPUWrapper() {
+
+}
+
+void LegacyGPUWrapper::poll() {
+ m_impl->process_metrics = m_impl->gpu->get_process_metrics();
+ m_impl->system_metrics = m_impl->gpu->get_system_metrics();
+}
+
+void LegacyGPUWrapper::add_pid(pid_t pid) {
+ m_impl->gpu->add_pid(pid);
+
+ if (auto* ptr = dynamic_cast(m_impl->gpu.get())) {
+ ptr->fdinfo.add_pid(pid);
+ }
+}
+
+#define GENERATE_SYS_METRIC_GETTER(name) \
+ decltype(std::declval().get_##name()) LegacyGPUWrapper::get_##name() { \
+ return m_impl->system_metrics.name; \
+ }
+
+#define GENERATE_PROC_METRIC_GETTER(name) \
+ decltype(std::declval().get_process_##name(0)) \
+ LegacyGPUWrapper::get_process_##name(pid_t pid) { \
+ return m_impl->process_metrics[pid].name; \
+ }
+
+GENERATE_SYS_METRIC_GETTER(load)
+GENERATE_SYS_METRIC_GETTER(vram_used)
+GENERATE_SYS_METRIC_GETTER(gtt_used)
+GENERATE_SYS_METRIC_GETTER(memory_total)
+GENERATE_SYS_METRIC_GETTER(memory_clock)
+GENERATE_SYS_METRIC_GETTER(memory_temp)
+GENERATE_SYS_METRIC_GETTER(temperature)
+GENERATE_SYS_METRIC_GETTER(junction_temperature)
+GENERATE_SYS_METRIC_GETTER(core_clock)
+GENERATE_SYS_METRIC_GETTER(voltage)
+GENERATE_SYS_METRIC_GETTER(power_usage)
+GENERATE_SYS_METRIC_GETTER(power_limit)
+GENERATE_SYS_METRIC_GETTER(is_apu)
+GENERATE_SYS_METRIC_GETTER(apu_cpu_power)
+GENERATE_SYS_METRIC_GETTER(apu_cpu_temp)
+GENERATE_SYS_METRIC_GETTER(is_power_throttled)
+GENERATE_SYS_METRIC_GETTER(is_current_throttled)
+GENERATE_SYS_METRIC_GETTER(is_temp_throttled)
+GENERATE_SYS_METRIC_GETTER(is_other_throttled)
+GENERATE_SYS_METRIC_GETTER(fan_speed)
+GENERATE_SYS_METRIC_GETTER(fan_rpm)
+
+GENERATE_PROC_METRIC_GETTER(load)
+GENERATE_PROC_METRIC_GETTER(vram_used)
+GENERATE_PROC_METRIC_GETTER(gtt_used)
+
+struct LegacyFDInfoWrapper::Impl {
+ Impl(const std::string& drm_node) : fdinfo(drm_node) {}
+ ~Impl() {}
+
+ FDInfoWrapper fdinfo;
+};
+
+LegacyFDInfoWrapper::LegacyFDInfoWrapper(const std::string& drm_node) {
+ m_impl = std::make_unique(drm_node);
+}
+
+LegacyFDInfoWrapper::~LegacyFDInfoWrapper() {}
+
+void LegacyFDInfoWrapper::add_pid(pid_t pid) {
+ m_impl->fdinfo.add_pid(pid);
+}
+
+void LegacyFDInfoWrapper::poll_all() {
+ m_impl->fdinfo.poll_all();
+}
+
+float LegacyFDInfoWrapper::get_memory_used(pid_t pid, const std::string& key) {
+ return m_impl->fdinfo.get_memory_used(pid, key);
+}
diff --git a/mangohud-next/legacy_gpu_wrapper/wrapper.hpp b/mangohud-next/legacy_gpu_wrapper/wrapper.hpp
new file mode 100644
index 0000000000..e3ef8dbc7f
--- /dev/null
+++ b/mangohud-next/legacy_gpu_wrapper/wrapper.hpp
@@ -0,0 +1,73 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+class LegacyGPUWrapper {
+public:
+ LegacyGPUWrapper(
+ const std::string& driver, const std::string& drm_node, const std::string& pci_dev,
+ uint16_t vendor_id, uint16_t device_id
+ );
+
+ ~LegacyGPUWrapper();
+
+ void poll();
+ void add_pid(pid_t pid);
+
+ // System-related functions
+ int get_load();
+
+ float get_vram_used();
+ float get_gtt_used();
+ float get_memory_total();
+ int get_memory_clock();
+ int get_memory_temp();
+
+ int get_temperature();
+ int get_junction_temperature();
+
+ int get_core_clock();
+ int get_voltage();
+
+ float get_power_usage();
+ float get_power_limit();
+
+ bool get_is_apu();
+ float get_apu_cpu_power();
+ int get_apu_cpu_temp();
+
+ bool get_is_power_throttled();
+ bool get_is_current_throttled();
+ bool get_is_temp_throttled();
+ bool get_is_other_throttled();
+
+ int get_fan_speed();
+ bool get_fan_rpm();
+
+ // Process-related functions
+ int get_process_load(pid_t pid);
+ float get_process_vram_used(pid_t pid);
+ float get_process_gtt_used(pid_t pid);
+
+private:
+ struct Impl;
+ std::unique_ptr m_impl;
+};
+
+
+class LegacyFDInfoWrapper {
+public:
+ LegacyFDInfoWrapper(const std::string& drm_node);
+ ~LegacyFDInfoWrapper();
+
+ void add_pid(pid_t pid);
+ void poll_all();
+ float get_memory_used(pid_t pid, const std::string& key);
+
+private:
+ struct Impl;
+ std::unique_ptr m_impl;
+};
diff --git a/mangohud-next/server/metrics/fdinfo.cpp b/mangohud-next/server/metrics/fdinfo.cpp
index 705f867c75..9488f40d56 100644
--- a/mangohud-next/server/metrics/fdinfo.cpp
+++ b/mangohud-next/server/metrics/fdinfo.cpp
@@ -9,6 +9,7 @@ namespace fs = std::filesystem;
using namespace std::chrono_literals;
FDInfoBase::FDInfoBase(const std::string& drm_node, const pid_t pid) : drm_node(drm_node), pid(pid) {
+ card_node = get_card_node();
init();
}
@@ -72,10 +73,10 @@ std::vector FDInfoBase::find_fds() {
continue;
}
+ // comparison to both renderD* and card* is required because
// for some reason supertuxkart opens /dev/dri/card and not renderD
// inside podman container.
- // this is only for testing, so remove it later
- if (link.filename() != drm_node && link.string().substr(0, 13) != "/dev/dri/card")
+ if (link.filename() != drm_node && link.filename() != card_node)
continue;
fds.push_back(entry.path().filename());
@@ -121,6 +122,29 @@ void FDInfoBase::open_fds(const std::vector& fds) {
SPDLOG_DEBUG("Received {} ids, opened {} unique ids", fds.size(), total);
}
+std::string FDInfoBase::get_card_node() {
+ const std::string device = "/sys/class/drm/" + drm_node + "/device/drm";
+
+ if (!std::filesystem::exists(device)) {
+ SPDLOG_DEBUG("drm dir doesn't exist for {}", drm_node);
+ return "";
+ }
+
+ // Find first dir which starts with name "card"
+ for (const auto& entry : fs::directory_iterator(device)) {
+ std::filesystem::path path = entry.path();
+
+ if (path.filename().string().substr(0, 4) == "card") {
+ const std::string filename = path.filename();
+ SPDLOG_DEBUG("found card node for {}: {}", drm_node, filename);
+ return filename;
+ }
+ }
+
+ SPDLOG_DEBUG("didn't find card node for {}", drm_node);
+ return "";
+}
+
void FDInfoWrapper::add_pid(pid_t pid) {
std::unique_lock lock(pids_mutex);
diff --git a/mangohud-next/server/metrics/fdinfo.hpp b/mangohud-next/server/metrics/fdinfo.hpp
index 75a30b45d6..250c3ee8b2 100644
--- a/mangohud-next/server/metrics/fdinfo.hpp
+++ b/mangohud-next/server/metrics/fdinfo.hpp
@@ -13,9 +13,11 @@ class FDInfoBase {
private:
std::vector fds_streams;
chrono_timer last_init;
+ std::string card_node;
std::vector find_fds();
void open_fds(const std::vector& fds);
+ std::string get_card_node();
public:
const std::string drm_node;
diff --git a/mangohud-next/server/metrics/gpu/gpu.cpp b/mangohud-next/server/metrics/gpu/gpu.cpp
index d28994d11d..c8a6f69d94 100644
--- a/mangohud-next/server/metrics/gpu/gpu.cpp
+++ b/mangohud-next/server/metrics/gpu/gpu.cpp
@@ -2,189 +2,6 @@
#include
#include "gpu.hpp"
-#include "intel/i915/i915.hpp"
-#include "intel/xe/xe.hpp"
-#include "amdgpu/amdgpu.hpp"
-#include "nvidia/nvidia.hpp"
-#include "panfrost.hpp"
-#include "panthor.hpp"
-#include "msm/dpu.hpp"
-#include "msm/kgsl.hpp"
-#include "../common/helpers.hpp"
-
-GPUS::GPUS() {
- std::set gpu_entries;
-
- for (const auto& entry : fs::directory_iterator("/sys/class/drm")) {
- if (!entry.is_directory())
- continue;
-
- std::string node_name = entry.path().filename().string();
-
- // Check if the directory is a render node (e.g., renderD128, renderD129, etc.)
- if (node_name.find("renderD") == 0 && node_name.length() > 7) {
- // Ensure the rest of the string after "renderD" is numeric
- std::string render_number = node_name.substr(7);
- if (std::all_of(render_number.begin(), render_number.end(), ::isdigit)) {
- gpu_entries.emplace(node_name); // Store the render entry
- }
- }
- }
-
- // Now process the sorted GPU entries
- uint8_t /*idx = 0,*/ total_active = 0;
-
- for (const auto& drm_node : gpu_entries) {
- const std::string path = "/sys/class/drm/" + drm_node;
- const std::string driver = get_driver(path);
-
- {
- const std::string* d =
- std::find(std::begin(supported_drivers), std::end(supported_drivers), driver);
-
- if (d == std::end(supported_drivers)) {
- SPDLOG_WARN(
- "node \"{}\" is using driver \"{}\" which is unsupported by MangoHud. Skipping...",
- drm_node, driver
- );
- continue;
- }
- }
-
- std::string device_address = get_pci_device_address(path); // Store the result
- const char* pci_dev = device_address.c_str();
-
- uint32_t vendor_id = 0;
- uint32_t device_id = 0;
-
- if (!device_address.empty())
- {
- try {
- vendor_id = std::stoul(read_line("/sys/bus/pci/devices/" + device_address + "/vendor"), nullptr, 16);
- } catch(...) {
- SPDLOG_ERROR("stoul failed on: {}", "/sys/bus/pci/devices/" + device_address + "/vendor");
- }
-
- try {
- device_id = std::stoul(read_line("/sys/bus/pci/devices/" + device_address + "/device"), nullptr, 16);
- } catch (...) {
- SPDLOG_ERROR("stoul failed on: {}", "/sys/bus/pci/devices/" + device_address + "/device");
- }
- }
-
- std::shared_ptr gpu;
-
- if (driver == "i915") {
- gpu = std::make_shared(drm_node, pci_dev, vendor_id, device_id);
- } else if (driver == "xe") {
- gpu = std::make_shared(drm_node, pci_dev, vendor_id, device_id);
- } else if (driver == "amdgpu") {
- gpu = std::make_shared(drm_node, pci_dev, vendor_id, device_id);
- } else if (driver == "nvidia") {
- gpu = std::make_shared(drm_node, pci_dev, vendor_id, device_id);
-
- if (Nvidia* ptr = dynamic_cast(gpu.get())) {
- if (!ptr->nvml_available) {
- SPDLOG_WARN(
- "NVML is not loaded. Nvidia metrics are not available!. "
- "Skipping node {}.", drm_node
- );
-
- continue;
- }
- }
- } else if (driver == "panfrost") {
- gpu = std::make_shared(drm_node, pci_dev, vendor_id, device_id);
- } else if (driver == "panthor") {
- gpu = std::make_shared(drm_node, pci_dev, vendor_id, device_id);
- } else if (driver == "msm_dpu") {
- gpu = std::make_shared(drm_node, pci_dev, vendor_id, device_id);
- } else if (driver == "msm_drm") {
- gpu = std::make_shared(drm_node, pci_dev, vendor_id, device_id);
- } else {
- continue;
- }
-
- {
- std::lock_guard lock(available_gpus_m);
- available_gpus.push_back(gpu);
- }
- // if (params->gpu_list.size() == 1 && params->gpu_list[0] == idx++)
- // gpu->is_active = true;
-
- // if (!params->pci_dev.empty() && pci_dev == params->pci_dev)
- // gpu->is_active = true;
-
- SPDLOG_DEBUG("GPU Found: drm_node: {}, driver: {}, vendor_id: {:x} device_id: {:x} pci_dev: {}", drm_node, driver, vendor_id, device_id, pci_dev);
-
- if (gpu->is_active) {
- SPDLOG_INFO("Set {} as active GPU (driver={} id={:x}:{:x} pci_dev={})", drm_node, driver, vendor_id, device_id, pci_dev);
- total_active++;
- }
- }
-
- if (total_active < 2)
- return;
-
- std::lock_guard lock(available_gpus_m);
- for (auto& gpu : available_gpus) {
- if (!gpu->is_active)
- continue;
-
- SPDLOG_WARN(
- "You have more than 1 active GPU, check if you use both pci_dev "
- "and gpu_list. If you use fps logging, MangoHud will log only "
- "this GPU: name = {}, vendor = {:x}, pci_dev = {}",
- gpu->drm_node, gpu->vendor_id, gpu->pci_dev
- );
-
- break;
- }
-}
-
-std::vector> GPUS::available() const {
- std::lock_guard lock(available_gpus_m);
- return available_gpus;
-}
-
-std::string GPUS::get_pci_device_address(const std::string& drm_card_path) {
- // /sys/class/drm/renderD128/device/subsystem -> /sys/bus/pci
- auto subsystem = fs::canonical(drm_card_path + "/device/subsystem").string();
- auto idx = subsystem.rfind("/") + 1; // /sys/bus/pci
- // ^
- // |- find this guy
- if (subsystem.substr(idx) != "pci")
- return "";
-
- // /sys/class/drm/renderD128/device
- // convert to
- // /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:01.0/0000:03:00.0
- auto pci_addr = fs::read_symlink(drm_card_path + "/device").string();
- idx = pci_addr.rfind("/") + 1; // /sys/.../0000:03:00.0
- // ^
- // |- find this guy
-
- return pci_addr.substr(idx); // 0000:03:00.0
-}
-
-std::string GPUS::get_driver(const std::string& drm_card_path) {
- std::string path = drm_card_path + "/device/driver";
-
- if (!fs::exists(path)) {
- SPDLOG_ERROR("{} doesn't exist", path);
- return "";
- }
-
- if (!fs::is_symlink(path)) {
- SPDLOG_ERROR("{} is not a symlink (it should be)", path);
- return "";
- }
-
- std::string driver = fs::read_symlink(path).string();
- driver = driver.substr(driver.rfind("/") + 1);
-
- return driver;
-}
void GPU::check_pids_existence() {
std::set pids_to_delete;
diff --git a/mangohud-next/server/metrics/gpu/gpu.hpp b/mangohud-next/server/metrics/gpu/gpu.hpp
index c8def31611..f4d1fd7351 100644
--- a/mangohud-next/server/metrics/gpu/gpu.hpp
+++ b/mangohud-next/server/metrics/gpu/gpu.hpp
@@ -20,6 +20,12 @@
using namespace std::chrono_literals;
namespace fs = std::filesystem;
+#ifdef MANGOHUD_LEGACY
+// MangoHud legacy also has class named GPU, and linker resolves legacy's GPU destructor call
+// to next's GPU destructor which leads to SEGFAULT
+#define GPU NextGPU
+#endif
+
class GPU {
public:
const std::string drm_node;
@@ -103,20 +109,3 @@ class GPU {
virtual float get_process_vram_used(pid_t pid) { return 0.f; }
virtual float get_process_gtt_used(pid_t pid) { return 0.f; }
};
-
-class GPUS {
-private:
- mutable std::mutex available_gpus_m;
- std::vector> available_gpus;
-
- std::string get_pci_device_address(const std::string& drm_card_path);
- std::string get_driver(const std::string& drm_card_path);
-
- const std::array supported_drivers = {
- "amdgpu", "nvidia", "i915", "xe", "panfrost", "msm_dpu", "msm_drm"
- };
-
-public:
- GPUS();
- std::vector> available() const;
-};
diff --git a/mangohud-next/server/metrics/gpu/gpus.cpp b/mangohud-next/server/metrics/gpu/gpus.cpp
new file mode 100644
index 0000000000..a4a0001cdb
--- /dev/null
+++ b/mangohud-next/server/metrics/gpu/gpus.cpp
@@ -0,0 +1,185 @@
+#include "intel/i915/i915.hpp"
+#include "intel/xe/xe.hpp"
+#include "amdgpu/amdgpu.hpp"
+#include "nvidia/nvidia.hpp"
+#include "panfrost.hpp"
+#include "panthor.hpp"
+#include "msm/dpu.hpp"
+#include "msm/kgsl.hpp"
+#include "../common/helpers.hpp"
+
+#include "gpus.hpp"
+
+GPUS::GPUS() {
+ std::set gpu_entries;
+
+ for (const auto& entry : fs::directory_iterator("/sys/class/drm")) {
+ if (!entry.is_directory())
+ continue;
+
+ std::string node_name = entry.path().filename().string();
+
+ // Check if the directory is a render node (e.g., renderD128, renderD129, etc.)
+ if (node_name.find("renderD") == 0 && node_name.length() > 7) {
+ // Ensure the rest of the string after "renderD" is numeric
+ std::string render_number = node_name.substr(7);
+ if (std::all_of(render_number.begin(), render_number.end(), ::isdigit)) {
+ gpu_entries.emplace(node_name); // Store the render entry
+ }
+ }
+ }
+
+ // Now process the sorted GPU entries
+ uint8_t /*idx = 0,*/ total_active = 0;
+
+ for (const auto& drm_node : gpu_entries) {
+ const std::string path = "/sys/class/drm/" + drm_node;
+ const std::string driver = get_driver(path);
+
+ {
+ const std::string* d =
+ std::find(std::begin(supported_drivers), std::end(supported_drivers), driver);
+
+ if (d == std::end(supported_drivers)) {
+ SPDLOG_WARN(
+ "node \"{}\" is using driver \"{}\" which is unsupported by MangoHud. Skipping...",
+ drm_node, driver
+ );
+ continue;
+ }
+ }
+
+ std::string device_address = get_pci_device_address(path); // Store the result
+ const char* pci_dev = device_address.c_str();
+
+ uint32_t vendor_id = 0;
+ uint32_t device_id = 0;
+
+ if (!device_address.empty())
+ {
+ try {
+ vendor_id = std::stoul(read_line("/sys/bus/pci/devices/" + device_address + "/vendor"), nullptr, 16);
+ } catch(...) {
+ SPDLOG_ERROR("stoul failed on: {}", "/sys/bus/pci/devices/" + device_address + "/vendor");
+ }
+
+ try {
+ device_id = std::stoul(read_line("/sys/bus/pci/devices/" + device_address + "/device"), nullptr, 16);
+ } catch (...) {
+ SPDLOG_ERROR("stoul failed on: {}", "/sys/bus/pci/devices/" + device_address + "/device");
+ }
+ }
+
+ std::shared_ptr gpu;
+
+ if (driver == "i915") {
+ gpu = std::make_shared(drm_node, pci_dev, vendor_id, device_id);
+ } else if (driver == "xe") {
+ gpu = std::make_shared(drm_node, pci_dev, vendor_id, device_id);
+ } else if (driver == "amdgpu") {
+ gpu = std::make_shared(drm_node, pci_dev, vendor_id, device_id);
+ } else if (driver == "nvidia") {
+ gpu = std::make_shared(drm_node, pci_dev, vendor_id, device_id);
+
+ if (Nvidia* ptr = dynamic_cast(gpu.get())) {
+ if (!ptr->nvml_available) {
+ SPDLOG_WARN(
+ "NVML is not loaded. Nvidia metrics are not available!. "
+ "Skipping node {}.", drm_node
+ );
+
+ continue;
+ }
+ }
+ } else if (driver == "panfrost") {
+ gpu = std::make_shared(drm_node, pci_dev, vendor_id, device_id);
+ } else if (driver == "panthor") {
+ gpu = std::make_shared(drm_node, pci_dev, vendor_id, device_id);
+ } else if (driver == "msm_dpu") {
+ gpu = std::make_shared(drm_node, pci_dev, vendor_id, device_id);
+ } else if (driver == "msm_drm") {
+ gpu = std::make_shared(drm_node, pci_dev, vendor_id, device_id);
+ } else {
+ continue;
+ }
+
+ {
+ std::lock_guard lock(available_gpus_m);
+ available_gpus.push_back(gpu);
+ }
+ // if (params->gpu_list.size() == 1 && params->gpu_list[0] == idx++)
+ // gpu->is_active = true;
+
+ // if (!params->pci_dev.empty() && pci_dev == params->pci_dev)
+ // gpu->is_active = true;
+
+ SPDLOG_DEBUG("GPU Found: drm_node: {}, driver: {}, vendor_id: {:x} device_id: {:x} pci_dev: {}", drm_node, driver, vendor_id, device_id, pci_dev);
+
+ if (gpu->is_active) {
+ SPDLOG_INFO("Set {} as active GPU (driver={} id={:x}:{:x} pci_dev={})", drm_node, driver, vendor_id, device_id, pci_dev);
+ total_active++;
+ }
+ }
+
+ if (total_active < 2)
+ return;
+
+ std::lock_guard lock(available_gpus_m);
+ for (auto& gpu : available_gpus) {
+ if (!gpu->is_active)
+ continue;
+
+ SPDLOG_WARN(
+ "You have more than 1 active GPU, check if you use both pci_dev "
+ "and gpu_list. If you use fps logging, MangoHud will log only "
+ "this GPU: name = {}, vendor = {:x}, pci_dev = {}",
+ gpu->drm_node, gpu->vendor_id, gpu->pci_dev
+ );
+
+ break;
+ }
+}
+
+std::vector> GPUS::available() const {
+ std::lock_guard lock(available_gpus_m);
+ return available_gpus;
+}
+
+std::string GPUS::get_pci_device_address(const std::string& drm_card_path) {
+ // /sys/class/drm/renderD128/device/subsystem -> /sys/bus/pci
+ auto subsystem = fs::canonical(drm_card_path + "/device/subsystem").string();
+ auto idx = subsystem.rfind("/") + 1; // /sys/bus/pci
+ // ^
+ // |- find this guy
+ if (subsystem.substr(idx) != "pci")
+ return "";
+
+ // /sys/class/drm/renderD128/device
+ // convert to
+ // /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:01.0/0000:03:00.0
+ auto pci_addr = fs::read_symlink(drm_card_path + "/device").string();
+ idx = pci_addr.rfind("/") + 1; // /sys/.../0000:03:00.0
+ // ^
+ // |- find this guy
+
+ return pci_addr.substr(idx); // 0000:03:00.0
+}
+
+std::string GPUS::get_driver(const std::string& drm_card_path) {
+ std::string path = drm_card_path + "/device/driver";
+
+ if (!fs::exists(path)) {
+ SPDLOG_ERROR("{} doesn't exist", path);
+ return "";
+ }
+
+ if (!fs::is_symlink(path)) {
+ SPDLOG_ERROR("{} is not a symlink (it should be)", path);
+ return "";
+ }
+
+ std::string driver = fs::read_symlink(path).string();
+ driver = driver.substr(driver.rfind("/") + 1);
+
+ return driver;
+}
diff --git a/mangohud-next/server/metrics/gpu/gpus.hpp b/mangohud-next/server/metrics/gpu/gpus.hpp
new file mode 100644
index 0000000000..e85571b860
--- /dev/null
+++ b/mangohud-next/server/metrics/gpu/gpus.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include "gpu.hpp"
+
+class GPUS {
+private:
+ mutable std::mutex available_gpus_m;
+ std::vector> available_gpus;
+
+ std::string get_pci_device_address(const std::string& drm_card_path);
+ std::string get_driver(const std::string& drm_card_path);
+
+ const std::array supported_drivers = {
+ "amdgpu", "nvidia", "i915", "xe", "panfrost", "msm_dpu", "msm_drm"
+ };
+
+public:
+ GPUS();
+ std::vector> available() const;
+};
diff --git a/mangohud-next/server/metrics/gpu/meson.build b/mangohud-next/server/metrics/gpu/meson.build
index ace8ae126f..efcb9d2d72 100644
--- a/mangohud-next/server/metrics/gpu/meson.build
+++ b/mangohud-next/server/metrics/gpu/meson.build
@@ -1,9 +1,7 @@
-libdrm_dep = dependency('libdrm')
-libcap_dep = dependency('libcap')
-
gpu_lib = static_library(
'gpu',
files('gpu.cpp',
+ 'gpus.cpp',
'amdgpu/amdgpu.cpp',
'amdgpu/gpu_metrics.cpp',
'intel/i915/i915.cpp',
@@ -14,7 +12,8 @@ gpu_lib = static_library(
'msm/kgsl.cpp',
'nvidia/nvidia.cpp',
'nvidia/nvml_loader.cpp',
- 'panthor.cpp'),
+ 'panthor.cpp',
+ 'panfrost.cpp'),
include_directories: '../',
dependencies: [libdrm_dep,
libcap_dep,
diff --git a/mangohud-next/server/metrics/panfrost.cpp b/mangohud-next/server/metrics/gpu/panfrost.cpp
similarity index 100%
rename from mangohud-next/server/metrics/panfrost.cpp
rename to mangohud-next/server/metrics/gpu/panfrost.cpp
diff --git a/mangohud-next/server/metrics/panfrost.hpp b/mangohud-next/server/metrics/gpu/panfrost.hpp
similarity index 100%
rename from mangohud-next/server/metrics/panfrost.hpp
rename to mangohud-next/server/metrics/gpu/panfrost.hpp
diff --git a/mangohud-next/server/metrics/hwmon.cpp b/mangohud-next/server/metrics/hwmon.cpp
index 031b625fa4..a46c8a00aa 100644
--- a/mangohud-next/server/metrics/hwmon.cpp
+++ b/mangohud-next/server/metrics/hwmon.cpp
@@ -106,19 +106,30 @@ std::string HwmonBase::find_hwmon_dir_by_name(const std::string& name) {
auto hwmon_dir = entry.path().string();
auto hwmon_name = hwmon_dir + "/name";
+ SPDLOG_TRACE("Opening {}", hwmon_name);
+
std::ifstream name_stream(hwmon_name);
std::string name_content;
- if (!name_stream.is_open())
+ if (!name_stream.is_open()) {
+ SPDLOG_TRACE("Failed to open {}", hwmon_name);
continue;
+ }
std::getline(name_stream, name_content);
std::smatch matches;
std::regex rx(name);
- if (!std::regex_match(name_content, matches, rx))
+ if (!std::regex_search(name_content, matches, rx)) {
+ SPDLOG_TRACE(
+ "'{}' sensor name '{}' doesn't match regex '{}'",
+ hwmon_dir, name_content, name
+ );
continue;
+ }
+
+ SPDLOG_TRACE("'{}' sensor name '{}' matches regex '{}'", hwmon_dir, name_content, name);
// return the first sensor with specified name
return hwmon_dir;
diff --git a/mangohud-next/server/metrics/meson.build b/mangohud-next/server/metrics/meson.build
index 02c3b06cb8..2dcb949c39 100644
--- a/mangohud-next/server/metrics/meson.build
+++ b/mangohud-next/server/metrics/meson.build
@@ -7,7 +7,6 @@ metrics_lib = static_library(
'fdinfo.cpp',
'hwmon.cpp',
'memory.cpp',
- 'panfrost.cpp',
'metrics.cpp'),
dependencies: [spdlog_dep,
vkbootstrap_dep,
diff --git a/mangohud-next/server/metrics/metrics.h b/mangohud-next/server/metrics/metrics.h
index c809b13a85..ece80299b3 100644
--- a/mangohud-next/server/metrics/metrics.h
+++ b/mangohud-next/server/metrics/metrics.h
@@ -6,7 +6,7 @@
#include
#include
#include "../config.h"
-#include "gpu/gpu.hpp"
+#include "gpu/gpus.hpp"
#include "cpu/cpu.hpp"
#include "exec.h"
#include "../../ipc/ipc.h"
diff --git a/meson.build b/meson.build
index 18cef1dc6b..dc7e26eab3 100644
--- a/meson.build
+++ b/meson.build
@@ -308,6 +308,10 @@ if get_option('mangoapp')
glfw3_dep = dependency('glfw3')
endif
+libdrm_dep = dependency('libdrm')
+libcap_dep = dependency('libcap')
+
+subdir('mangohud-next/legacy_gpu_wrapper')
subdir('src')
if get_option('include_doc')
diff --git a/src/amdgpu.cpp b/src/amdgpu.cpp
index 388fb5e418..6341a401e4 100644
--- a/src/amdgpu.cpp
+++ b/src/amdgpu.cpp
@@ -298,9 +298,15 @@ void AMDGPU::get_samples_and_copy(struct amdgpu_common_metrics metrics_buffer[ME
get_sysfs_metrics();
#ifndef TEST_ONLY
- metrics.proc_vram_used = fdinfo_helper->amdgpu_helper_get_proc_vram();
+ if (HUDElements.g_gamescopePid > 0 && HUDElements.g_gamescopePid != pid) {
+ pid = HUDElements.g_gamescopePid;
+ fdinfo.add_pid(pid);
+ }
#endif
+ fdinfo.poll_all();
+ metrics.proc_vram_used = fdinfo.get_memory_used(pid, "drm-memory-vram");
+
if (gpu_metrics_is_valid) {
UPDATE_METRIC_AVERAGE(gpu_load_percent);
UPDATE_METRIC_AVERAGE_FLOAT(average_gfx_power_w);
@@ -506,7 +512,8 @@ void AMDGPU::get_sysfs_metrics() {
}
}
-AMDGPU::AMDGPU(std::string pci_dev, uint32_t device_id, uint32_t vendor_id) {
+AMDGPU::AMDGPU(std::string pci_dev, uint32_t device_id, uint32_t vendor_id, std::string drm_node)
+: fdinfo(drm_node) {
this->pci_dev = pci_dev;
this->device_id = device_id;
this->vendor_id = vendor_id;
@@ -553,11 +560,9 @@ AMDGPU::AMDGPU(std::string pci_dev, uint32_t device_id, uint32_t vendor_id) {
}
}
- throttling = std::make_shared(0x1002);
-#ifndef TEST_ONLY
- fdinfo_helper = std::make_unique("amdgpu", pci_dev, "", /*called_from_amdgpu_cpp=*/ true);
-#endif
+ fdinfo.add_pid(pid);
+ throttling = std::make_shared(0x1002);
thread = std::thread(&AMDGPU::metrics_polling_thread, this);
pthread_setname_np(thread.native_handle(), "mangohud-amdgpu");
}
diff --git a/src/amdgpu.h b/src/amdgpu.h
index 9d3088523d..8f3da5a774 100644
--- a/src/amdgpu.h
+++ b/src/amdgpu.h
@@ -13,9 +13,7 @@
#include
#include "gpu_metrics_util.h"
-#ifndef TEST_ONLY
-#include "gpu_fdinfo.h"
-#endif
+#include "../mangohud-next/legacy_gpu_wrapper/wrapper.hpp"
#define NUM_HBM_INSTANCES 4
#define TEMP_HOTSPOT_BIT 36ull
@@ -479,7 +477,7 @@ class AMDGPU {
bool is_apu = false;
std::shared_ptr throttling;
- AMDGPU(std::string pci_dev, uint32_t device_id, uint32_t vendor_id);
+ AMDGPU(std::string pci_dev, uint32_t device_id, uint32_t vendor_id, std::string drm_node);
~AMDGPU() {
stop_thread = true;
@@ -520,15 +518,13 @@ class AMDGPU {
std::atomic paused{false};
std::mutex metrics_mutex;
gpu_metrics metrics;
+ pid_t pid = getpid();
+ LegacyFDInfoWrapper fdinfo;
struct amdgpu_common_metrics amdgpu_common_metrics;
struct gpu_metrics_v3_0 previous_metrics{};
#define V3_THROTTLING_DELTA(name) \
((amdgpu_metrics)->throttle_residency_##name - (previous_metrics).throttle_residency_##name)
-#ifndef TEST_ONLY
- std::unique_ptr fdinfo_helper;
-#endif
-
void get_sysfs_metrics();
void metrics_polling_thread();
};
diff --git a/src/gpu.cpp b/src/gpu.cpp
index 3c00a69037..b036d4890e 100644
--- a/src/gpu.cpp
+++ b/src/gpu.cpp
@@ -1,3 +1,4 @@
+#include
#include "gpu.h"
#include "file_utils.h"
#include "hud_elements.h"
diff --git a/src/gpu.h b/src/gpu.h
index 675d64f8a9..d0687bf7cc 100644
--- a/src/gpu.h
+++ b/src/gpu.h
@@ -40,14 +40,14 @@ class GPU {
nvidia = std::make_unique(pci_dev);
if (vendor_id == 0x1002)
- amdgpu = std::make_unique(pci_dev, device_id, vendor_id);
+ amdgpu = std::make_unique(pci_dev, device_id, vendor_id, drm_node);
if (
driver == "i915" || driver == "xe" ||
driver == "panfrost" || driver == "panthor" ||
driver == "msm_dpu" || driver == "msm_drm"
)
- fdinfo = std::make_unique(driver, pci_dev, drm_node);
+ fdinfo = std::make_unique(driver, pci_dev, drm_node, device_id, vendor_id);
}
gpu_metrics get_metrics() {
diff --git a/src/gpu_fdinfo.cpp b/src/gpu_fdinfo.cpp
index 0fdd99e836..0a761e4eba 100644
--- a/src/gpu_fdinfo.cpp
+++ b/src/gpu_fdinfo.cpp
@@ -1,705 +1,22 @@
-#include "gpu_fdinfo.h"
-
#ifndef TEST_ONLY
#include "hud_elements.h"
#endif
-namespace fs = ghc::filesystem;
-
-void GPU_fdinfo::find_fd()
-{
- fdinfo.clear();
- fdinfo_data.clear();
-
- auto dir = std::string("/proc/") + std::to_string(pid) + "/fdinfo";
- auto path = fs::path(dir);
-
- SPDLOG_TRACE("fdinfo_dir = {}", dir);
-
- if (!fs::exists(path)) {
- SPDLOG_DEBUG("{} does not exist", path.string());
- return;
- }
-
- // Here we store client-ids, if ids match, we dont open this file,
- // because it will have same readings and it becomes a duplicate
- std::set client_ids;
- int total = 0;
-
- for (const auto& entry : fs::directory_iterator(path)) {
- auto fd_path = entry.path().string();
- auto file = std::ifstream(fd_path);
-
- if (!file.is_open())
- continue;
-
- std::string driver, pdev, client_id;
-
- for (std::string line; std::getline(file, line);) {
- size_t colon = line.find(":");
-
- if (line[0] == ' ' || line[0] == '\t')
- continue;
-
- if (colon == std::string::npos || colon + 2 >= line.length())
- continue;
-
- auto key = line.substr(0, colon);
- auto val = line.substr(key.length() + 2);
-
- if (key == "drm-driver")
- driver = val;
- else if (key == "drm-pdev")
- pdev = val;
- else if (key == "drm-client-id")
- client_id = val;
- }
-
- if (!driver.empty() && driver == module) {
- total++;
- SPDLOG_TRACE(
- "driver = \"{}\", pdev = \"{}\", "
- "client_id = \"{}\", client_id_exists = \"{}\"",
- driver, pdev,
- client_id, client_ids.find(client_id) != client_ids.end()
- );
- }
-
- if (
- driver.empty() || client_id.empty() ||
- driver != module || pdev != pci_dev ||
- client_ids.find(client_id) != client_ids.end()
- )
- continue;
-
- client_ids.insert(client_id);
- open_fdinfo_fd(fd_path);
- }
-
- SPDLOG_TRACE(
- "Found {} total fds. Opened {} unique fds.",
- total,
- fdinfo.size()
- );
-}
-
-void GPU_fdinfo::open_fdinfo_fd(std::string path) {
- fdinfo.push_back(std::ifstream(path));
- fdinfo_data.push_back({});
-}
-
-void GPU_fdinfo::gather_fdinfo_data() {
- for (size_t i = 0; i < fdinfo.size(); i++) {
- fdinfo[i].clear();
- fdinfo[i].seekg(0);
-
- for (std::string line; std::getline(fdinfo[i], line);) {
- size_t colon = line.find(":");
-
- if (line[0] == ' ' || line[0] == '\t')
- continue;
-
- if (colon == std::string::npos || colon + 2 >= line.length())
- continue;
-
- auto key = line.substr(0, line.find(":"));
- auto val = line.substr(key.length() + 2);
- fdinfo_data[i][key] = val;
- }
- }
-}
-
-uint64_t GPU_fdinfo::get_gpu_time()
-{
- uint64_t total = 0;
-
- if (module == "panfrost")
- return get_gpu_time_panfrost();
-
- for (auto& fd : fdinfo_data) {
- auto time = fd[drm_engine_type];
-
- if (time.empty())
- continue;
-
- total += std::stoull(time);
- }
-
- return total;
-}
-
-uint64_t GPU_fdinfo::get_gpu_time_panfrost() {
- uint64_t total = 0;
-
- for (auto& fd : fdinfo_data) {
- auto frag = fd["drm-engine-fragment"];
- auto vert = fd["drm-engine-vertex-tiler"];
-
- if (!frag.empty())
- total += std::stoull(frag);
-
- if (!vert.empty())
- total += std::stoull(vert);
- }
-
- return total;
-}
-
-float GPU_fdinfo::get_memory_used()
-{
- uint64_t total = 0;
-
- for (auto& fd : fdinfo_data) {
- auto mem = fd[drm_memory_type];
-
- if (mem.empty())
- continue;
-
- std::string unit = mem.substr(mem.rfind(" ") + 1);
- uint64_t val = std::stoull(mem);
-
- if (unit == "KiB")
- val *= 1024;
- else if (unit == "MiB")
- val *= 1024 * 1024;
- else if (unit == "GiB")
- val *= 1024 * 1024 * 1024;
-
- total += val;
- }
-
- return static_cast(total) / 1024 / 1024 / 1024;
-}
-
-void GPU_fdinfo::find_hwmon_sensors()
-{
- std::string hwmon;
-
- if (module == "msm")
- hwmon = find_hwmon_sensor_dir("gpu");
- else if (module == "panfrost" || module == "panthor")
- hwmon = find_hwmon_sensor_dir("gpu_thermal");
- else
- hwmon = find_hwmon_dir();
-
- if (hwmon.empty()) {
- SPDLOG_DEBUG("hwmon: failed to find hwmon directory");
- return;
- }
-
- SPDLOG_DEBUG("hwmon: checking \"{}\" directory", hwmon);
-
- for (const auto &entry : fs::directory_iterator(hwmon)) {
- auto filename = entry.path().filename().string();
-
- for (auto& hs : hwmon_sensors) {
- auto key = hs.first;
- auto sensor = &hs.second;
- std::smatch matches;
-
- if (
- !std::regex_match(filename, matches, sensor->rx) ||
- matches.size() != 2
- )
- continue;
-
- auto cur_id = std::stoull(matches[1].str());
-
- if (sensor->filename.empty() || cur_id < sensor->id) {
- sensor->filename = entry.path().string();
- sensor->id = cur_id;
- }
- }
- }
-
- for (auto& hs : hwmon_sensors) {
- auto key = hs.first;
- auto sensor = &hs.second;
-
- if (sensor->filename.empty()) {
- SPDLOG_DEBUG("hwmon: {} reading not found at {}", key, hwmon);
- continue;
- }
-
- SPDLOG_DEBUG("hwmon: {} reading found at {}", key, sensor->filename);
-
- sensor->stream.open(sensor->filename);
-
- if (!sensor->stream.good()) {
- SPDLOG_DEBUG(
- "hwmon: failed to open {} reading {}",
- key, sensor->filename
- );
- continue;
- }
- }
-}
-
-std::string GPU_fdinfo::find_hwmon_dir() {
- std::string d = "/sys/class/drm/" + drm_node + "/device/hwmon";
-
- if (!fs::exists(d)) {
- SPDLOG_DEBUG("hwmon: hwmon directory \"{}\" doesn't exist", d);
- return "";
- }
-
- auto dir_iterator = fs::directory_iterator(d);
- auto hwmon = dir_iterator->path().string();
-
- if (hwmon.empty()) {
- SPDLOG_DEBUG("hwmon: hwmon directory \"{}\" is empty.", d);
- return "";
- }
-
- return hwmon;
-}
-
-std::string GPU_fdinfo::find_hwmon_sensor_dir(std::string name) {
- std::string d = "/sys/class/hwmon/";
-
- if (!fs::exists(d))
- return "";
-
- for (const auto &entry : fs::directory_iterator(d)) {
- auto hwmon_dir = entry.path().string();
- auto hwmon_name = hwmon_dir + "/name";
-
- std::ifstream name_stream(hwmon_name);
- std::string name_content;
-
- if (!name_stream.is_open())
- continue;
-
- std::getline(name_stream, name_content);
-
- if (name_content.find(name) == std::string::npos)
- continue;
-
- // return the first gpu sensor
- return hwmon_dir;
- }
-
- return "";
-}
-
-void GPU_fdinfo::get_current_hwmon_readings()
-{
- for (auto& hs : hwmon_sensors) {
- auto key = hs.first;
- auto sensor = &hs.second;
-
- if (!sensor->stream.is_open())
- continue;
-
- sensor->stream.seekg(0);
-
- std::stringstream ss;
- ss << sensor->stream.rdbuf();
-
- if (ss.str().empty())
- continue;
-
- sensor->val = std::stoull(ss.str());
- }
-}
-
-float GPU_fdinfo::get_power_usage()
-{
- if (!hwmon_sensors["power"].filename.empty())
- return static_cast(hwmon_sensors["power"].val) / 1'000'000;
-
- float now = hwmon_sensors["energy"].val;
-
- // Initialize value for the first time, otherwise delta will be very large
- // and your gpu power usage will be like 1 million watts for a second.
- if (this->last_power == 0.f)
- this->last_power = now;
-
- float delta = now - this->last_power;
- delta /= METRICS_UPDATE_PERIOD_MS / 1000.f;
-
- this->last_power = now;
-
- return delta / 1'000'000;
-}
-
-int GPU_fdinfo::get_xe_load()
-{
- double load = 0;
-
- for (auto& fd : fdinfo_data) {
- std::string client_id = fd["drm-client-id"];
- std::string cur_cycles_str = fd["drm-cycles-rcs"];
- std::string cur_total_cycles_str = fd["drm-total-cycles-rcs"];
-
- if (
- client_id.empty() || cur_cycles_str.empty() ||
- cur_total_cycles_str.empty()
- )
- continue;
-
- auto cur_cycles = std::stoull(cur_cycles_str);
- auto cur_total_cycles = std::stoull(cur_total_cycles_str);
-
- if (prev_xe_cycles.find(client_id) == prev_xe_cycles.end()) {
- prev_xe_cycles[client_id] = { cur_cycles, cur_total_cycles };
- continue;
- }
-
- auto prev_cycles = prev_xe_cycles[client_id].first;
- auto prev_total_cycles = prev_xe_cycles[client_id].second;
-
- auto delta_cycles = cur_cycles - prev_cycles;
- auto delta_total_cycles = cur_total_cycles - prev_total_cycles;
-
- prev_xe_cycles[client_id] = { cur_cycles, cur_total_cycles };
-
- if (delta_cycles <= 0 || delta_total_cycles <= 0)
- continue;
-
- auto fd_load = static_cast(delta_cycles) / delta_total_cycles * 100;
- load += fd_load;
- }
-
- if (load > 100.f)
- load = 100.f;
-
- return std::lround(load);
-}
-
-int GPU_fdinfo::get_gpu_load()
-{
- if (module == "xe")
- return get_xe_load();
- else if (module == "msm_drm")
- return get_kgsl_load();
-
- uint64_t now = os_time_get_nano();
- uint64_t gpu_time_now = get_gpu_time();
-
- if (previous_time == 0) {
- previous_gpu_time = gpu_time_now;
- previous_time = now;
-
- return 0;
- }
-
- float delta_time = now - previous_time;
- float delta_gpu_time = gpu_time_now - previous_gpu_time;
-
- int result = delta_gpu_time / delta_time * 100;
-
- if (result > 100)
- result = 100;
-
- previous_gpu_time = gpu_time_now;
- previous_time = now;
-
- return std::round(result);
-}
-
-void GPU_fdinfo::find_i915_gt_dir()
-{
- std::string device = "/sys/bus/pci/devices/" + pci_dev + "/drm";
-
- // Find first dir which starts with name "card"
- for (const auto& entry : fs::directory_iterator(device)) {
- auto path = entry.path().string();
-
- if (path.substr(device.size() + 1, 4) == "card") {
- device = path;
- break;
- }
- }
-
- auto gpu_clock_path = device + "/gt_act_freq_mhz";
- gpu_clock_stream.open(gpu_clock_path);
-
- if (!gpu_clock_stream.good())
- SPDLOG_WARN("Intel i915 gt dir: failed to open {}", device);
-
- // Assuming gt0 since all recent GPUs have the RCS engine on gt0,
- // and latest GPUs need Xe anyway
- auto throttle_folder = device + "/gt/gt0/throttle_";
- auto throttle_status_path = throttle_folder + "reason_status";
-
- throttle_status_stream.open(throttle_status_path);
- if (!throttle_status_stream.good()) {
- SPDLOG_WARN("Intel i915 gt dir: failed to open {}", throttle_status_path);
- } else {
- load_xe_i915_throttle_reasons(throttle_folder,
- intel_throttle_power,
- throttle_power_streams);
-
- load_xe_i915_throttle_reasons(throttle_folder,
- intel_throttle_current,
- throttle_current_streams);
-
- load_xe_i915_throttle_reasons(throttle_folder,
- intel_throttle_temp,
- throttle_temp_streams);
- }
-}
-
-void GPU_fdinfo::find_xe_gt_dir()
-{
- std::string device = "/sys/bus/pci/devices/" + pci_dev + "/tile0";
-
- if (!fs::exists(device)) {
- SPDLOG_WARN(
- "\"{}\" doesn't exist. GPU clock will be unavailable.",
- device
- );
- return;
- }
-
- bool has_rcs = true;
-
- // Check every "gt" dir if it has "engines/rcs" inside
- for (const auto& entry : fs::directory_iterator(device)) {
- auto path = entry.path().string();
-
- if (path.substr(device.size() + 1, 2) != "gt")
- continue;
-
- SPDLOG_DEBUG("Checking \"{}\" for rcs.", path);
-
- if (!fs::exists(path + "/engines/rcs")) {
- SPDLOG_DEBUG("Skipping \"{}\" because rcs doesn't exist.", path);
- continue;
- }
-
- SPDLOG_DEBUG("Found rcs in \"{}\"", path);
- has_rcs = true;
- device = path;
- break;
-
- }
-
- if (!has_rcs) {
- SPDLOG_WARN(
- "rcs not found inside \"{}\". GPU clock will not be available.",
- device
- );
- return;
- }
-
- auto gpu_clock_path = device + "/freq0/act_freq";
- gpu_clock_stream.open(gpu_clock_path);
-
- if (!gpu_clock_stream.good())
- SPDLOG_WARN("Intel xe gt dir: failed to open {}", gpu_clock_path);
-
- auto throttle_folder = device + "/freq0/throttle/";
- auto throttle_status_path = throttle_folder + "status";
-
- throttle_status_stream.open(throttle_status_path);
- if (!throttle_status_stream.good()) {
- SPDLOG_WARN("Intel xe gt dir: failed to open {}", throttle_status_path);
- } else {
- load_xe_i915_throttle_reasons(throttle_folder,
- intel_throttle_power,
- throttle_power_streams);
-
- load_xe_i915_throttle_reasons(throttle_folder,
- intel_throttle_current,
- throttle_current_streams);
-
- load_xe_i915_throttle_reasons(throttle_folder,
- intel_throttle_temp,
- throttle_temp_streams);
- }
-}
-
-void GPU_fdinfo::load_xe_i915_throttle_reasons(
- std::string throttle_folder,
- std::vector throttle_reasons,
- std::vector& throttle_reason_streams
-) {
- for (const auto& throttle_reason : throttle_reasons) {
- std::string throttle_path = throttle_folder + throttle_reason;
- if (!fs::exists(throttle_path)) {
- SPDLOG_WARN(
- "Intel xe/i915 gt dir: Throttle file {} not found",
- throttle_path
- );
- continue;
- }
- auto throttle_stream = std::ifstream(throttle_path);
- if (!throttle_stream.good()) {
- SPDLOG_WARN("Intel xe/i915 gt dir: failed to open {}", throttle_path);
- continue;
- }
- throttle_reason_streams.push_back(std::move(throttle_stream));
- }
-}
-
-int GPU_fdinfo::get_gpu_clock()
-{
- if (module == "panfrost" || module == "panthor")
- return get_gpu_clock_mali();
-
- if (!gpu_clock_stream.is_open())
- return 0;
-
- std::string clock_str;
-
- gpu_clock_stream.seekg(0);
-
- std::getline(gpu_clock_stream, clock_str);
-
- if (clock_str.empty())
- return 0;
-
- return std::stoi(clock_str);
-}
-
-int GPU_fdinfo::get_gpu_clock_mali() {
- if (fdinfo_data.empty())
- return 0;
-
- std::string key;
-
- if (module == "panfrost")
- key = "drm-curfreq-fragment";
- else if (module == "panthor")
- key = "drm-curfreq-panthor";
-
- std::string freq_str = fdinfo_data[0][key];
-
- if (freq_str.empty())
- return 0;
-
- float freq = std::stoull(freq_str) / 1'000'000;
-
- return std::round(freq);
-}
+#include "gpu_fdinfo.h"
-bool GPU_fdinfo::check_throttle_reasons(
- std::vector& throttle_reason_streams)
+GPU_fdinfo::GPU_fdinfo(
+ const std::string driver, const std::string pci_dev, const std::string drm_node,
+ uint32_t device_id, uint32_t vendor_id
+) : driver(driver), pci_dev(pci_dev), drm_node(drm_node),
+ gpu(driver, drm_node, pci_dev, vendor_id, device_id)
{
- for (auto& throttle_reason_stream : throttle_reason_streams) {
- std::string throttle_reason_str;
- throttle_reason_stream.seekg(0);
- std::getline(throttle_reason_stream, throttle_reason_str);
+ SPDLOG_DEBUG("GPU driver is \"{}\"", driver);
- if (throttle_reason_str == "1")
- return true;
- }
+ gpu.add_pid(pid);
- return false;
-}
-
-int GPU_fdinfo::get_throttling_status()
-{
- if (!throttle_status_stream.is_open())
- return 0;
-
- std::string throttle_status_str;
- throttle_status_stream.seekg(0);
- std::getline(throttle_status_stream, throttle_status_str);
-
- if (throttle_status_str != "1")
- return 0;
-
- int reasons =
- check_throttle_reasons(throttle_power_streams) * GPU_throttle_status::POWER +
- check_throttle_reasons(throttle_current_streams) * GPU_throttle_status::CURRENT +
- check_throttle_reasons(throttle_temp_streams) * GPU_throttle_status::TEMP;
- // No throttle reasons for OTHER currently
- if (reasons == 0)
- reasons |= GPU_throttle_status::OTHER;
-
- return reasons;
-}
-
-float GPU_fdinfo::amdgpu_helper_get_proc_vram() {
-#ifndef TEST_ONLY
- if (HUDElements.g_gamescopePid > 0 && HUDElements.g_gamescopePid != pid)
- {
- pid = HUDElements.g_gamescopePid;
- find_fd();
- }
-#endif
-
- // Recheck fds every 10secs, fixes Mass Effect 1, maybe some others too
- {
- auto t = os_time_get_nano() / 1'000'000;
- if (t - fdinfo_last_update_ms >= 10'000) {
- find_fd();
- fdinfo_last_update_ms = t;
- }
- }
-
- gather_fdinfo_data();
-
- return get_memory_used();
-}
-
-void GPU_fdinfo::init_kgsl() {
- const std::string sys_path = "/sys/class/kgsl/kgsl-3d0";
-
- try {
- if (!fs::exists(sys_path)) {
- SPDLOG_WARN("kgsl: {} is not found. kgsl stats will not work!", sys_path);
- return;
- }
- } catch (fs::filesystem_error& ex) {
- SPDLOG_WARN("kgsl: {}", ex.what());
- return;
- }
-
- for (std::string metric : {"gpu_busy_percentage", "temp", "clock_mhz" }) {
- std::string p = sys_path + "/" + metric;
-
- if (!fs::exists(p)) {
- SPDLOG_WARN("kgsl: {} is not found", p);
- continue;
- }
-
- SPDLOG_DEBUG("kgsl: {} found", p);
-
- if (metric == "clock_mhz")
- gpu_clock_stream.open(p);
- else
- kgsl_streams[metric].open(p);
- }
-}
-
-int GPU_fdinfo::get_kgsl_load() {
- std::ifstream* s = &kgsl_streams["gpu_busy_percentage"];
-
- if (!s->is_open())
- return 0;
-
- std::string usage_str;
-
- s->seekg(0);
-
- std::getline(*s, usage_str);
-
- if (usage_str.empty())
- return 0;
-
- return std::stoi(usage_str);
-}
-
-int GPU_fdinfo::get_kgsl_temp() {
- std::ifstream* s = &kgsl_streams["temp"];
-
- if (!s->is_open())
- return 0;
-
- std::string temp_str;
-
- s->seekg(0);
-
- std::getline(*s, temp_str);
-
- if (temp_str.empty())
- return 0;
-
- return std::round(std::stoi(temp_str) / 1'000.f);
+ thread = std::thread(&GPU_fdinfo::main_thread, this);
+ // "mangohud-gpufdinfo" wouldn't fit in the 15 byte limit
+ pthread_setname_np(thread.native_handle(), "mangohud-gpufd");
}
void GPU_fdinfo::main_thread()
@@ -709,59 +26,52 @@ void GPU_fdinfo::main_thread()
cond_var.wait(lock, [this]() { return !paused || stop_thread; });
#ifndef TEST_ONLY
- if (HUDElements.g_gamescopePid > 0 && HUDElements.g_gamescopePid != pid)
- {
+ if (HUDElements.g_gamescopePid > 0 && HUDElements.g_gamescopePid != pid) {
pid = HUDElements.g_gamescopePid;
- find_fd();
+ gpu.add_pid(pid);
}
#endif
- // Recheck fds every 10secs, fixes Mass Effect 1, maybe some others too
- {
- auto t = os_time_get_nano() / 1'000'000;
- if (t - fdinfo_last_update_ms >= 10'000) {
- find_fd();
- fdinfo_last_update_ms = t;
- }
- }
-
- gather_fdinfo_data();
- get_current_hwmon_readings();
-
- metrics.load = get_gpu_load();
- metrics.proc_vram_used = get_memory_used();
-
- metrics.powerUsage = get_power_usage();
- metrics.powerLimit = static_cast(hwmon_sensors["power_limit"].val) / 1'000'000;
-
- metrics.CoreClock = get_gpu_clock();
- metrics.voltage = hwmon_sensors["voltage"].val;
-
- if (module == "msm_drm")
- metrics.temp = get_kgsl_temp();
- else
- metrics.temp = hwmon_sensors["temp"].val / 1000.f;
-
- metrics.memory_temp = hwmon_sensors["vram_temp"].val / 1000.f;
-
- metrics.fan_speed = hwmon_sensors["fan_speed"].val;
- metrics.fan_rpm = true; // Fan data is pulled from hwmon
-
- int throttling = get_throttling_status();
- metrics.is_power_throttled = throttling & GPU_throttle_status::POWER;
- metrics.is_current_throttled = throttling & GPU_throttle_status::CURRENT;
- metrics.is_temp_throttled = throttling & GPU_throttle_status::TEMP;
- metrics.is_other_throttled = throttling & GPU_throttle_status::OTHER;
+ gpu.poll();
+
+ if (driver == "msm_drm") {
+ metrics.load = gpu.get_load();
+ } else {
+ metrics.load = gpu.get_process_load(pid);
+ }
+
+ metrics.temp = gpu.get_temperature();
+ metrics.junction_temp = gpu.get_junction_temperature();
+ metrics.memory_temp = gpu.get_memory_temp();
+ metrics.sys_vram_used = gpu.get_vram_used();
+ metrics.proc_vram_used = gpu.get_process_vram_used(pid);
+ metrics.memoryTotal = gpu.get_memory_total();
+ metrics.MemClock = gpu.get_memory_clock();
+ metrics.CoreClock = gpu.get_core_clock();
+ metrics.powerUsage = gpu.get_power_usage();
+ metrics.powerLimit = gpu.get_power_limit();
+ metrics.is_power_throttled = gpu.get_is_power_throttled();
+ metrics.is_current_throttled = gpu.get_is_current_throttled();
+ metrics.is_temp_throttled = gpu.get_is_temp_throttled();
+ metrics.is_other_throttled = gpu.get_is_other_throttled();
+ metrics.gtt_used = gpu.get_gtt_used();
+ metrics.fan_speed = gpu.get_fan_speed();
+ metrics.voltage = gpu.get_voltage();
+ metrics.fan_rpm = gpu.get_fan_rpm();
SPDLOG_DEBUG(
- "pci_dev = {}, pid = {}, module = {}, "
- "load = {}, proc_vram = {}, power = {}, "
- "core = {}, temp = {}, fan = {}, "
- "voltage = {}",
- pci_dev, pid, module,
- metrics.load, metrics.proc_vram_used, metrics.powerUsage,
- metrics.CoreClock, metrics.temp, metrics.fan_speed,
- metrics.voltage
+ "pci_dev = {}, pid = {}, driver = {}, "
+ "load = {}, temp = {}, junction_temp = {}, memory_temp = {}, sys_vram_used = {}, "
+ "proc_vram_used = {}, memoryTotal = {}, MemClock = {}, CoreClock = {}, powerUsage = {}, "
+ "powerLimit = {}, is_power_throttled = {}, is_current_throttled = {}, "
+ "is_temp_throttled = {}, is_other_throttled = {}, gtt_used = {}, fan_speed = {}, "
+ "voltage = {}, fan_rpm = {}",
+ pci_dev, pid, driver,
+ metrics.load, metrics.temp, metrics.junction_temp, metrics.memory_temp,
+ metrics.sys_vram_used, metrics.proc_vram_used, metrics.memoryTotal, metrics.MemClock,
+ metrics.CoreClock, metrics.powerUsage, metrics.powerLimit, metrics.is_power_throttled,
+ metrics.is_current_throttled, metrics.is_temp_throttled, metrics.is_other_throttled,
+ metrics.gtt_used, metrics.fan_speed, metrics.voltage, metrics.fan_rpm
);
std::this_thread::sleep_for(
diff --git a/src/gpu_fdinfo.h b/src/gpu_fdinfo.h
index 97e62e5265..5dff3af10c 100644
--- a/src/gpu_fdinfo.h
+++ b/src/gpu_fdinfo.h
@@ -1,49 +1,14 @@
#pragma once
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include