Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gpu_fdinfo bug fixes, almost full support for Intel is finally here #1499

Merged
merged 12 commits into from
Dec 16, 2024
Merged
7 changes: 5 additions & 2 deletions data/MangoHud.conf
Original file line number Diff line number Diff line change
Expand Up @@ -316,9 +316,12 @@ text_outline
# battery_color=FF9078
# network_color=E07B85

### Specify GPU with PCI bus ID for AMDGPU and NVML stats
### Specify GPU with PCI bus ID
### Set to 'domain:bus:slot.function'
# pci_dev=0:0a:0.0
### Example:
### $ lspci | grep A770
### 03:00.0 VGA compatible controller: Intel Corporation DG2 [Arc A770] (rev 08)
# pci_dev=0000:03:00.0

### Blacklist
# blacklist=
Expand Down
123 changes: 45 additions & 78 deletions src/gpu.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "gpu.h"
#include <cstdint>
#include <inttypes.h>
#include <memory>
#include <functional>
Expand All @@ -22,16 +23,17 @@ GPUS::GPUS(overlay_params* params) : params(params) {
std::vector<std::string> gpu_entries;

for (const auto& entry : fs::directory_iterator("/sys/class/drm")) {
if (entry.is_directory()) {
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.push_back(node_name); // Store the render entry
}
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.push_back(node_name); // Store the render entry
}
}
}
Expand All @@ -44,6 +46,8 @@ GPUS::GPUS(overlay_params* params) : params(params) {
});

// Now process the sorted GPU entries
uint8_t idx = 0, total_active = 0;

for (const auto& node_name : gpu_entries) {
std::string path = "/sys/class/drm/" + node_name;
std::string device_address = get_pci_device_address(path); // Store the result
Expand All @@ -56,20 +60,48 @@ GPUS::GPUS(overlay_params* params) : params(params) {
} 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> ptr = std::make_shared<GPU>(node_name, vendor_id, device_id, pci_dev);

if (params->gpu_list.size() == 1 && params->gpu_list[0] == idx++)
ptr->is_active = true;

if (!params->pci_dev.empty() && pci_dev == params->pci_dev)
ptr->is_active = true;

available_gpus.emplace_back(ptr);

SPDLOG_DEBUG("GPU Found: node_name: {}, vendor_id: {:x} device_id: {:x} pci_dev: {}", node_name, vendor_id, device_id, pci_dev);
flightlessmango marked this conversation as resolved.
Show resolved Hide resolved

if (ptr->is_active) {
SPDLOG_INFO("Set {} as active GPU (id={:x}:{:x} pci_dev={})", node_name, vendor_id, device_id, pci_dev);
total_active++;
}
}

if (total_active < 2)
return;

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->name, gpu->vendor_id, gpu->pci_dev
);

break;
}

find_active_gpu();
}

std::string GPU::is_i915_or_xe() {
Expand Down Expand Up @@ -120,74 +152,9 @@ std::string GPUS::get_pci_device_address(const std::string& drm_card_path) {
}
}

void GPUS::find_active_gpu() {
pid_t pid = getpid();
std::string fdinfo_dir = "/proc/" + std::to_string(pid) + "/fdinfo/";
bool active_gpu_found = false;

for (const auto& entry : fs::directory_iterator(fdinfo_dir)) {
if (entry.is_regular_file()) {
std::ifstream file(entry.path().string());
std::string line;
std::string drm_pdev;
bool has_drm_driver = false;
bool has_drm_engine_gfx = false;

while (std::getline(file, line)) {
if (line.find("drm-driver:") != std::string::npos) {
has_drm_driver = true;
}
if (line.find("drm-pdev:") != std::string::npos) {
drm_pdev = line.substr(line.find(":") + 1);
drm_pdev.erase(0, drm_pdev.find_first_not_of(" \t"));
}
if (line.find("drm-engine-gfx:") != std::string::npos) {
uint64_t gfx_time = std::stoull(line.substr(line.find(":") + 1));
if (gfx_time > 0) {
has_drm_engine_gfx = true;
}
}
}

if (has_drm_driver && has_drm_engine_gfx) {
for (const auto& gpu : available_gpus) {
if (gpu->pci_dev == drm_pdev) {
gpu->is_active = true;
SPDLOG_DEBUG("Active GPU Found: node_name: {}, pci_dev: {}", gpu->name, gpu->pci_dev);
flightlessmango marked this conversation as resolved.
Show resolved Hide resolved
} else {
gpu->is_active = false;
}
}
return;
}
}
}

// NVIDIA GPUs will not show up in fdinfo so we use NVML instead to find the active GPU
// This will not work for older NVIDIA GPUs
if (!active_gpu_found) {
#ifdef HAVE_NVML
for (const auto& gpu : available_gpus) {
// NVIDIA vendor ID is 0x10de
if (gpu->vendor_id == 0x10de && gpu->nvidia->nvml_available) {
for (auto& pid : gpu->nvidia_pids()) {
if (pid == getpid()) {
gpu->is_active = true;
SPDLOG_DEBUG("Active GPU Found: node_name: {}, pci_dev: {}", gpu->name, gpu->pci_dev);
flightlessmango marked this conversation as resolved.
Show resolved Hide resolved
return;
}
}

}
}
#endif
}
SPDLOG_DEBUG("failed to find active GPU");
}

int GPU::index_in_selected_gpus() {
auto selected_gpus = gpus->selected_gpus();
auto it = std::find_if(selected_gpus.begin(), selected_gpus.end(),
auto it = std::find_if(selected_gpus.begin(), selected_gpus.end(),
[this](const std::shared_ptr<GPU>& gpu) {
return gpu.get() == this;
});
Expand Down
50 changes: 30 additions & 20 deletions src/gpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ class GPU {
std::unique_ptr<NVIDIA> nvidia = nullptr;
std::unique_ptr<AMDGPU> amdgpu = nullptr;
std::unique_ptr<GPU_fdinfo> fdinfo = nullptr;
bool is_active;
bool is_active = false;
std::string pci_dev;
uint32_t vendor_id;
uint32_t vendor_id = 0;

GPU(std::string name, uint32_t vendor_id, uint32_t device_id, const char* pci_dev)
: name(name), pci_dev(pci_dev), vendor_id(vendor_id), device_id(device_id) {
Expand Down Expand Up @@ -123,7 +123,6 @@ class GPUS {
std::mutex mutex;
overlay_params* params;

void find_active_gpu();
GPUS(overlay_params* params);

void pause() {
Expand All @@ -137,18 +136,17 @@ class GPUS {
}

std::shared_ptr<GPU> active_gpu() {
if (!available_gpus.empty()){
for (auto gpu : available_gpus) {
if (gpu->is_active) {
return gpu;
}
}
}
// if no GPU is marked as active, just set it to the first one
if (available_gpus.size() > 0)
return available_gpus.front();
else
if (available_gpus.empty())
return nullptr;

for (auto gpu : available_gpus) {
if (gpu->is_active)
return gpu;
}

// if no GPU is marked as active, just set it to the last one
// because integrated gpus are usually first
return available_gpus.back();
}

void update_throttling() {
Expand All @@ -166,18 +164,30 @@ class GPUS {
std::vector<std::shared_ptr<GPU>> selected_gpus() {
std::lock_guard<std::mutex> lock(mutex);
std::vector<std::shared_ptr<GPU>> vec;

if (params->gpu_list.empty() && params->pci_dev.empty())
return available_gpus;

if (!params->gpu_list.empty()) {
for (unsigned index : params->gpu_list) {
if (index < available_gpus.size()) {
if (available_gpus[index])
vec.push_back(available_gpus[index]);
}
}
}

return vec;
}

if (!params->pci_dev.empty()) {
for (auto &gpu : available_gpus) {
if (gpu->pci_dev == params->pci_dev) {
vec.push_back(gpu);
return vec;
}
}
// if the user hasn't selected any GPUs, we use the active one
} else {
if (active_gpu())
vec.push_back(active_gpu());


return vec;
}

return vec;
Expand Down
Loading
Loading