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 -#include -#include -#include - -#ifdef TEST_ONLY -#include <../src/mesa/util/os_time.h> -#else -#include "mesa/util/os_time.h" -#endif - #include -#include - #include "gpu_metrics_util.h" - -struct hwmon_sensor { - std::regex rx; - std::ifstream stream; - std::string filename; - unsigned char id = 0; - uint64_t val = 0; -}; - -enum GPU_throttle_status : int { - POWER = 0b0001, - CURRENT = 0b0010, - TEMP = 0b0100, - OTHER = 0b1000, -}; +#include "../mangohud-next/legacy_gpu_wrapper/wrapper.hpp" class GPU_fdinfo { private: pid_t pid = getpid(); - const std::string module; + const std::string driver; const std::string pci_dev; const std::string drm_node; @@ -56,167 +21,15 @@ class GPU_fdinfo { struct gpu_metrics metrics; mutable std::mutex metrics_mutex; - std::vector fdinfo; - uint64_t fdinfo_last_update_ms = 0; - - std::map hwmon_sensors; - - std::string drm_engine_type = "EMPTY"; - std::string drm_memory_type = "EMPTY"; - - std::vector> fdinfo_data; - void gather_fdinfo_data(); + LegacyGPUWrapper gpu; void main_thread(); - void find_fd(); - void open_fdinfo_fd(std::string path); - - int get_gpu_load(); - uint64_t get_gpu_time(); - - uint64_t previous_gpu_time = 0, previous_time = 0; - - std::vector xe_fdinfo_last_cycles; - std::map> prev_xe_cycles; - int get_xe_load(); - - float get_memory_used(); - - void find_hwmon_sensors(); - std::string find_hwmon_dir(); - std::string find_hwmon_sensor_dir(std::string name); - void get_current_hwmon_readings(); - - float get_power_usage(); - float last_power = 0; - - std::ifstream gpu_clock_stream; - void find_i915_gt_dir(); - void find_xe_gt_dir(); - int get_gpu_clock(); - - uint64_t get_gpu_time_panfrost(); - int get_gpu_clock_mali(); - - std::ifstream throttle_status_stream; - std::vector throttle_power_streams; - std::vector throttle_current_streams; - std::vector throttle_temp_streams; - bool check_throttle_reasons(std::vector &throttle_reason_streams); - int get_throttling_status(); - - const std::vector intel_throttle_power = {"reason_pl1", "reason_pl2"}; - const std::vector intel_throttle_current = {"reason_pl4", "reason_vr_tdc"}; - const std::vector intel_throttle_temp = { - "reason_prochot", "reason_ratl", "reason_thermal", "reason_vr_thermalert"}; - void load_xe_i915_throttle_reasons( - std::string throttle_folder, - std::vector throttle_reasons, - std::vector &throttle_reason_streams); - - std::map kgsl_streams; - void init_kgsl(); - int get_kgsl_load(); - int get_kgsl_temp(); - public: GPU_fdinfo( - const std::string module, const std::string pci_dev, const std::string drm_node, - const bool called_from_amdgpu_cpp=false - ) - : module(module) - , pci_dev(pci_dev) - , drm_node(drm_node) - { - SPDLOG_DEBUG("GPU driver is \"{}\"", module); - - find_fd(); - gather_fdinfo_data(); - - if (module == "i915") { - drm_engine_type = "drm-engine-render"; - drm_memory_type = "drm-resident-local0"; - } else if (module == "xe") { - drm_engine_type = "drm-total-cycles-rcs"; - drm_memory_type = "drm-resident-vram0"; - } else if (module == "amdgpu") { - drm_engine_type = "drm-engine-gfx"; - drm_memory_type = "drm-memory-vram"; - } else if (module == "msm_dpu") { - // msm driver does not report vram usage - drm_engine_type = "drm-engine-gpu"; - } else if (module == "msm_drm") { - init_kgsl(); - } else if (module == "panfrost") { - drm_engine_type = "drm-engine-fragment"; - drm_memory_type = "drm-resident-memory"; - } else if (module == "panthor") { - drm_engine_type = "drm-engine-panthor"; - drm_memory_type = "drm-resident-memory"; - } - - if (fdinfo_data.size() > 0 && - fdinfo_data[0].find(drm_memory_type) == fdinfo_data[0].end()) - { - auto old_type = drm_memory_type; - - if (module == "i915") - drm_memory_type = "drm-resident-system0"; - else if (module == "xe") - drm_memory_type = "drm-resident-gtt"; - - SPDLOG_DEBUG( - "\"{}\" is not found, you probably have an integrated GPU. " - "Using \"{}\"", old_type, drm_memory_type - ); - } - - SPDLOG_DEBUG( - "drm_engine_type = {}, drm_memory_type = {}", - drm_engine_type, drm_memory_type - ); - - if (called_from_amdgpu_cpp) - return; - - // i915: Documentation/ABI/testing/sysfs-driver-intel-i915-hwmon - // xe : Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon - - if (module == "i915") { - hwmon_sensors["voltage"] = { .rx = std::regex("in(0)_input") }; - hwmon_sensors["fan_speed"] = { .rx = std::regex("fan(1)_input") }; - hwmon_sensors["temp"] = { .rx = std::regex("temp(1)_input") }; - hwmon_sensors["energy"] = { .rx = std::regex("energy(1)_input") }; - hwmon_sensors["power_limit"] = { .rx = std::regex("power(1)_max") }; - } else if (module == "xe") { - hwmon_sensors["voltage"] = { .rx = std::regex("in(1)_input") }; - // technically, there are 3 fan sensors, but just pick first one - hwmon_sensors["fan_speed"] = { .rx = std::regex("fan(1)_input") }; - hwmon_sensors["temp"] = { .rx = std::regex("temp(2)_input") }; - hwmon_sensors["vram_temp"] = { .rx = std::regex("temp(3)_input") }; - hwmon_sensors["energy"] = { .rx = std::regex("energy(2)_input") }; - hwmon_sensors["power_limit"] = { .rx = std::regex("power(2)_max") }; - } else { - // For everyone else just guess - hwmon_sensors["voltage"] = { .rx = std::regex("in(\\d+)_input") }; - hwmon_sensors["fan_speed"] = { .rx = std::regex("fan(\\d+)_input") }; - hwmon_sensors["temp"] = { .rx = std::regex("temp(\\d+)_input") }; - hwmon_sensors["power"] = { .rx = std::regex("power(\\d+)_input") }; - hwmon_sensors["energy"] = { .rx = std::regex("energy(\\d+)_input") }; - } - - find_hwmon_sensors(); - - if (module == "i915") - find_i915_gt_dir(); - else if (module == "xe") - find_xe_gt_dir(); - - 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"); - } + const std::string driver, const std::string pci_dev, const std::string drm_node, + uint32_t device_id, uint32_t vendor_id + ); ~GPU_fdinfo() { stop_thread = true; @@ -240,6 +53,4 @@ class GPU_fdinfo { paused = false; cond_var.notify_one(); } - - float amdgpu_helper_get_proc_vram(); }; diff --git a/src/meson.build b/src/meson.build index d7e4b362d0..b84d9491d0 100644 --- a/src/meson.build +++ b/src/meson.build @@ -207,6 +207,7 @@ mangohud_static_lib = static_library( mangohud_version_dep], include_directories : [inc_common], link_args : link_args, + link_with: mangohud_legacy_gpu_wrapper, install_dir : libdir_mangohud, install : false )