MangoHud/src/overlay.cpp

822 lines
30 KiB
C++
Raw Normal View History

#include <sstream>
#include <iomanip>
2020-09-06 08:58:16 +00:00
#include <algorithm>
2021-09-23 12:53:58 +00:00
#include <thread>
#include <condition_variable>
2021-07-16 00:28:46 +00:00
#include <spdlog/spdlog.h>
#include <filesystem.h>
2020-03-10 05:19:18 +00:00
#include "overlay.h"
#include "logging.h"
2020-02-01 23:55:24 +00:00
#include "cpu.h"
#include "gpu.h"
2020-03-10 05:19:18 +00:00
#include "memory.h"
#include "timing.hpp"
#include "mesa/util/macros.h"
2020-09-06 08:58:16 +00:00
#include "string_utils.h"
2021-03-16 08:50:07 +00:00
#include "battery.h"
#include "string_utils.h"
#include "file_utils.h"
#include "gpu.h"
#include "logging.h"
#include "cpu.h"
#include "memory.h"
#include "pci_ids.h"
#include "timing.hpp"
2021-10-01 20:34:21 +00:00
#ifdef __linux__
#include <libgen.h>
#include <unistd.h>
#endif
namespace fs = ghc::filesystem;
#ifdef HAVE_DBUS
float g_overflow = 50.f /* 3333ms * 0.5 / 16.6667 / 2 (to edge and back) */;
#endif
2020-09-06 08:58:16 +00:00
string gpuString,wineVersion,wineProcess;
uint32_t deviceID;
bool gui_open = false;
2020-09-06 08:58:16 +00:00
struct benchmark_stats benchmark;
2020-09-06 11:52:18 +00:00
struct fps_limit fps_limit_stats {};
2020-09-23 10:23:29 +00:00
ImVec2 real_font_size;
2020-11-06 23:01:23 +00:00
std::vector<logData> graph_data;
2021-03-13 16:09:17 +00:00
const char* engines[] = {"Unknown", "OpenGL", "VULKAN", "DXVK", "VKD3D", "DAMAVAND", "ZINK", "WINED3D", "Feral3D", "ToGL"};
2020-02-04 08:39:27 +00:00
void update_hw_info(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID)
{
if (params.enabled[OVERLAY_PARAM_ENABLED_cpu_stats] || logger->is_active()) {
cpuStats.UpdateCPUData();
#ifdef __linux__
2020-11-12 12:19:22 +00:00
if (params.enabled[OVERLAY_PARAM_ENABLED_core_load] || params.enabled[OVERLAY_PARAM_ENABLED_cpu_mhz])
cpuStats.UpdateCoreMhz();
if (params.enabled[OVERLAY_PARAM_ENABLED_cpu_temp] || logger->is_active() || params.enabled[OVERLAY_PARAM_ENABLED_graphs])
cpuStats.UpdateCpuTemp();
2020-11-12 21:27:35 +00:00
if (params.enabled[OVERLAY_PARAM_ENABLED_cpu_power])
cpuStats.UpdateCpuPower();
2020-09-06 11:52:18 +00:00
#endif
}
if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats] || logger->is_active()) {
if (vendorID == 0x1002 && getAmdGpuInfo_actual)
getAmdGpuInfo_actual();
2020-06-21 11:44:12 +00:00
if (vendorID == 0x10de)
getNvidiaGpuInfo();
}
2020-06-21 11:44:12 +00:00
2021-10-01 20:34:21 +00:00
#ifdef __linux__
if (params.enabled[OVERLAY_PARAM_ENABLED_battery])
Battery_Stats.update();
2020-12-13 10:07:17 +00:00
if (params.enabled[OVERLAY_PARAM_ENABLED_ram] || params.enabled[OVERLAY_PARAM_ENABLED_swap] || logger->is_active())
update_meminfo();
if (params.enabled[OVERLAY_PARAM_ENABLED_procmem])
update_procmem();
if (params.enabled[OVERLAY_PARAM_ENABLED_io_read] || params.enabled[OVERLAY_PARAM_ENABLED_io_write])
getIoStats(&sw_stats.io);
2020-09-06 11:52:18 +00:00
#endif
2020-06-21 11:44:12 +00:00
currentLogData.gpu_load = gpu_info.load;
currentLogData.gpu_temp = gpu_info.temp;
currentLogData.gpu_core_clock = gpu_info.CoreClock;
currentLogData.gpu_mem_clock = gpu_info.MemClock;
currentLogData.gpu_vram_used = gpu_info.memoryUsed;
2021-02-07 06:18:54 +00:00
currentLogData.gpu_power = gpu_info.powerUsage;
2021-10-01 20:34:21 +00:00
#ifdef __linux__
currentLogData.ram_used = memused;
2020-09-06 11:52:18 +00:00
#endif
2020-06-21 11:44:12 +00:00
currentLogData.cpu_load = cpuStats.GetCPUDataTotal().percent;
currentLogData.cpu_temp = cpuStats.GetCPUDataTotal().temp;
2020-11-06 23:01:23 +00:00
// Save data for graphs
if (graph_data.size() > 50)
graph_data.erase(graph_data.begin());
2021-02-20 10:58:11 +00:00
graph_data.push_back(currentLogData);
logger->notify_data_valid();
2020-12-16 02:43:14 +00:00
HUDElements.update_exec();
2020-06-21 11:44:12 +00:00
}
2021-09-23 12:53:58 +00:00
struct hw_info_updater
{
bool quit = false;
std::thread thread {};
struct swapchain_stats* sw_stats = nullptr;
struct overlay_params* params = nullptr;
2021-09-23 12:53:58 +00:00
uint32_t vendorID;
bool update_hw_info_thread = false;
std::condition_variable cv_hwupdate;
std::mutex cv_m_hwupdate, m_hw_updating;
2021-09-23 12:53:58 +00:00
hw_info_updater()
2021-09-23 12:53:58 +00:00
{
thread = std::thread(&hw_info_updater::run, this);
}
~hw_info_updater()
{
quit = true;
cv_hwupdate.notify_all();
if (thread.joinable())
thread.join();
}
void update(struct swapchain_stats* sw_stats_, struct overlay_params* params_, uint32_t vendorID_)
2021-09-23 12:53:58 +00:00
{
std::unique_lock<std::mutex> lk(m_hw_updating);
sw_stats = sw_stats_;
params = params_;
vendorID = vendorID_;
2021-09-23 12:53:58 +00:00
update_hw_info_thread = true;
cv_hwupdate.notify_all();
}
void run(){
while (!quit){
std::unique_lock<std::mutex> lk(cv_m_hwupdate);
cv_hwupdate.wait(lk, [&]{ return update_hw_info_thread || quit; });
if (quit) break;
if (sw_stats && params)
{
std::unique_lock<std::mutex> lk(m_hw_updating);
update_hw_info(*sw_stats, *params, vendorID);
}
2021-09-23 12:53:58 +00:00
update_hw_info_thread = false;
}
}
};
static std::unique_ptr<hw_info_updater> hw_update_thread;
void stop_hw_updater()
{
if (hw_update_thread)
hw_update_thread.reset();
}
2020-03-10 05:19:18 +00:00
void update_hud_info(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID){
uint32_t f_idx = sw_stats.n_frames % ARRAY_SIZE(sw_stats.frames_stats);
uint64_t now = os_time_get_nano(); /* ns */
double elapsed = (double)(now - sw_stats.last_fps_update); /* ns */
fps = 1000000000.0 * sw_stats.n_frames_since_update / elapsed;
if (logger->is_active())
benchmark.fps_data.push_back(fps);
2020-03-10 05:19:18 +00:00
if (sw_stats.last_present_time) {
2020-03-24 19:38:08 +00:00
sw_stats.frames_stats[f_idx].stats[OVERLAY_PLOTS_frame_timing] =
2020-03-10 05:19:18 +00:00
now - sw_stats.last_present_time;
}
2021-06-19 22:58:01 +00:00
2021-06-10 17:35:01 +00:00
frametime = (now - sw_stats.last_present_time) / 1000;
if (elapsed >= params.fps_sampling_period) {
2021-09-23 12:53:58 +00:00
if (!hw_update_thread)
hw_update_thread = std::make_unique<hw_info_updater>();
hw_update_thread->update(&sw_stats, &params, vendorID);
2021-09-23 12:53:58 +00:00
sw_stats.fps = fps;
2020-03-14 17:11:34 +00:00
if (params.enabled[OVERLAY_PARAM_ENABLED_time]) {
std::time_t t = std::time(nullptr);
std::stringstream time;
time << std::put_time(std::localtime(&t), params.time_format.c_str());
sw_stats.time = time.str();
}
2020-03-14 17:11:34 +00:00
sw_stats.n_frames_since_update = 0;
sw_stats.last_fps_update = now;
2020-03-14 17:11:34 +00:00
}
if (params.log_interval == 0){
logger->try_log();
}
2020-03-10 05:19:18 +00:00
sw_stats.last_present_time = now;
sw_stats.n_frames++;
sw_stats.n_frames_since_update++;
2020-09-06 08:58:16 +00:00
}
2021-09-18 15:21:20 +00:00
void calculate_benchmark_data(){
2020-09-06 08:58:16 +00:00
vector<float> sorted = benchmark.fps_data;
std::sort(sorted.begin(), sorted.end());
benchmark.percentile_data.clear();
benchmark.total = 0.f;
for (auto fps_ : sorted){
benchmark.total = benchmark.total + fps_;
}
size_t max_label_size = 0;
2021-09-18 15:21:20 +00:00
for (std::string percentile : _params.benchmark_percentiles) {
2020-09-06 08:58:16 +00:00
float result;
// special case handling for a mean-based average
if (percentile == "AVG") {
result = benchmark.total / sorted.size();
} else {
// the percentiles are already validated when they're parsed from the config.
float fraction = parse_float(percentile) / 100;
result = sorted[(fraction * sorted.size()) - 1];
percentile += "%";
}
if (percentile.length() > max_label_size)
max_label_size = percentile.length();
benchmark.percentile_data.push_back({percentile, result});
}
for (auto& entry : benchmark.percentile_data) {
entry.first.append(max_label_size - entry.first.length(), ' ');
}
}
float get_time_stat(void *_data, int _idx)
{
struct swapchain_stats *data = (struct swapchain_stats *) _data;
if ((ARRAY_SIZE(data->frames_stats) - _idx) > data->n_frames)
return 0.0f;
int idx = ARRAY_SIZE(data->frames_stats) +
data->n_frames < ARRAY_SIZE(data->frames_stats) ?
_idx - data->n_frames :
_idx + data->n_frames;
idx %= ARRAY_SIZE(data->frames_stats);
/* Time stats are in us. */
return data->frames_stats[idx].stats[data->stat_selector] / data->time_dividor;
}
void position_layer(struct swapchain_stats& data, struct overlay_params& params, ImVec2 window_size)
{
unsigned width = ImGui::GetIO().DisplaySize.x;
unsigned height = ImGui::GetIO().DisplaySize.y;
float margin = 10.0f;
if (params.offset_x > 0 || params.offset_y > 0)
margin = 0.0f;
ImGui::SetNextWindowBgAlpha(params.background_alpha);
ImGui::SetNextWindowSize(window_size, ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8,-3));
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, params.alpha);
switch (params.position) {
case LAYER_POSITION_TOP_LEFT:
data.main_window_pos = ImVec2(margin + params.offset_x, margin + params.offset_y);
ImGui::SetNextWindowPos(data.main_window_pos, ImGuiCond_Always);
break;
case LAYER_POSITION_TOP_RIGHT:
data.main_window_pos = ImVec2(width - window_size.x - margin + params.offset_x, margin + params.offset_y);
ImGui::SetNextWindowPos(data.main_window_pos, ImGuiCond_Always);
break;
case LAYER_POSITION_MIDDLE_LEFT:
data.main_window_pos = ImVec2(margin + params.offset_x, height / 2 - window_size.y / 2 - margin + params.offset_y);
ImGui::SetNextWindowPos(data.main_window_pos, ImGuiCond_Always);
break;
case LAYER_POSITION_MIDDLE_RIGHT:
data.main_window_pos = ImVec2(width - window_size.x - margin + params.offset_x, height / 2 - window_size.y / 2 - margin + params.offset_y);
ImGui::SetNextWindowPos(data.main_window_pos, ImGuiCond_Always);
break;
case LAYER_POSITION_BOTTOM_LEFT:
data.main_window_pos = ImVec2(margin + params.offset_x, height - window_size.y - margin + params.offset_y);
ImGui::SetNextWindowPos(data.main_window_pos, ImGuiCond_Always);
break;
case LAYER_POSITION_BOTTOM_RIGHT:
data.main_window_pos = ImVec2(width - window_size.x - margin + params.offset_x, height - window_size.y - margin + params.offset_y);
ImGui::SetNextWindowPos(data.main_window_pos, ImGuiCond_Always);
break;
case LAYER_POSITION_TOP_CENTER:
data.main_window_pos = ImVec2((width / 2) - (window_size.x / 2), margin + params.offset_y);
ImGui::SetNextWindowPos(data.main_window_pos, ImGuiCond_Always);
break;
}
}
void right_aligned_text(ImVec4& col, float off_x, const char *fmt, ...)
{
ImVec2 pos = ImGui::GetCursorPos();
char buffer[32] {};
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
ImVec2 sz = ImGui::CalcTextSize(buffer);
ImGui::SetCursorPosX(pos.x + off_x - sz.x);
//ImGui::Text("%s", buffer);
ImGui::TextColored(col,"%s",buffer);
}
void center_text(const std::string& text)
2020-12-06 22:03:42 +00:00
{
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2 )- (ImGui::CalcTextSize(text.c_str()).x / 2));
2020-12-06 22:03:42 +00:00
}
float get_ticker_limited_pos(float pos, float tw, float& left_limit, float& right_limit)
{
//float cw = ImGui::GetContentRegionAvailWidth() * 3; // only table cell worth of width
float cw = ImGui::GetWindowContentRegionMax().x - ImGui::GetStyle().WindowPadding.x;
float new_pos_x = ImGui::GetCursorPosX();
left_limit = cw - tw + new_pos_x;
right_limit = new_pos_x;
if (cw < tw) {
new_pos_x += pos;
// acts as a delay before it starts scrolling again
if (new_pos_x < left_limit)
return left_limit;
else if (new_pos_x > right_limit)
return right_limit;
else
return new_pos_x;
}
return new_pos_x;
}
#ifdef HAVE_DBUS
void render_mpris_metadata(struct overlay_params& params, mutexed_metadata& meta, uint64_t frame_timing)
{
if (meta.meta.valid) {
auto color = ImGui::ColorConvertU32ToFloat4(params.media_player_color);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8,0));
ImGui::Dummy(ImVec2(0.0f, 20.0f));
//ImGui::PushFont(data.font1);
if (meta.ticker.needs_recalc) {
meta.ticker.formatted.clear();
meta.ticker.longest = 0;
for (const auto& f : params.media_player_format)
{
std::string str;
try
{
str = fmt::format(f,
fmt::arg("artist", meta.meta.artists),
fmt::arg("title", meta.meta.title),
fmt::arg("album", meta.meta.album));
}
2021-10-01 20:10:13 +00:00
catch (const fmt::format_error& err)
{
SPDLOG_ERROR("formatting error in '{}': {}", f, err.what());
}
float w = ImGui::CalcTextSize(str.c_str()).x;
meta.ticker.longest = std::max(meta.ticker.longest, w);
meta.ticker.formatted.push_back({str, w});
}
meta.ticker.needs_recalc = false;
}
float new_pos, left_limit = 0, right_limit = 0;
get_ticker_limited_pos(meta.ticker.pos, meta.ticker.longest, left_limit, right_limit);
if (meta.ticker.pos < left_limit - g_overflow * .5f) {
meta.ticker.dir = -1;
meta.ticker.pos = (left_limit - g_overflow * .5f) + 1.f /* random */;
} else if (meta.ticker.pos > right_limit + g_overflow) {
meta.ticker.dir = 1;
meta.ticker.pos = (right_limit + g_overflow) - 1.f /* random */;
}
meta.ticker.pos -= .5f * (frame_timing / 16666666.7f /* ns */) * meta.ticker.dir;
for (const auto& fmt : meta.ticker.formatted)
{
if (fmt.text.empty()) continue;
new_pos = get_ticker_limited_pos(meta.ticker.pos, fmt.width, left_limit, right_limit);
ImGui::SetCursorPosX(new_pos);
ImGui::TextColored(color, "%s", fmt.text.c_str());
}
if (!meta.meta.playing) {
ImGui::TextColored(color, "(paused)");
}
//ImGui::PopFont();
ImGui::PopStyleVar();
}
}
#endif
void render_benchmark(swapchain_stats& data, struct overlay_params& params, ImVec2& window_size, unsigned height, Clock::time_point now){
// TODO, FIX LOG_DURATION FOR BENCHMARK
int benchHeight = (2 + benchmark.percentile_data.size()) * real_font_size.x + 10.0f + 58;
ImGui::SetNextWindowSize(ImVec2(window_size.x, benchHeight), ImGuiCond_Always);
if (height - (window_size.y + data.main_window_pos.y + 5) < benchHeight)
ImGui::SetNextWindowPos(ImVec2(data.main_window_pos.x, data.main_window_pos.y - benchHeight - 5), ImGuiCond_Always);
else
ImGui::SetNextWindowPos(ImVec2(data.main_window_pos.x, data.main_window_pos.y + window_size.y + 5), ImGuiCond_Always);
float display_time = std::chrono::duration<float>(now - logger->last_log_end()).count();
static float display_for = 10.0f;
float alpha;
if (params.background_alpha != 0){
if (display_for >= display_time){
alpha = display_time * params.background_alpha;
if (alpha >= params.background_alpha){
ImGui::SetNextWindowBgAlpha(params.background_alpha);
}else{
ImGui::SetNextWindowBgAlpha(alpha);
}
} else {
alpha = 6.0 - display_time * params.background_alpha;
if (alpha >= params.background_alpha){
ImGui::SetNextWindowBgAlpha(params.background_alpha);
}else{
ImGui::SetNextWindowBgAlpha(alpha);
}
}
} else {
if (display_for >= display_time){
alpha = display_time * 0.0001;
ImGui::SetNextWindowBgAlpha(params.background_alpha);
} else {
alpha = 6.0 - display_time * 0.0001;
ImGui::SetNextWindowBgAlpha(params.background_alpha);
}
}
ImGui::Begin("Benchmark", &gui_open, ImGuiWindowFlags_NoDecoration);
static const char* finished = "Logging Finished";
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2 )- (ImGui::CalcTextSize(finished).x / 2));
ImGui::TextColored(ImVec4(1.0, 1.0, 1.0, alpha / params.background_alpha), "%s", finished);
ImGui::Dummy(ImVec2(0.0f, 8.0f));
char duration[20];
snprintf(duration, sizeof(duration), "Duration: %.1fs", std::chrono::duration<float>(logger->last_log_end() - logger->last_log_begin()).count());
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2 )- (ImGui::CalcTextSize(duration).x / 2));
ImGui::TextColored(ImVec4(1.0, 1.0, 1.0, alpha / params.background_alpha), "%s", duration);
for (auto& data_ : benchmark.percentile_data){
char buffer[20];
snprintf(buffer, sizeof(buffer), "%s %.1f", data_.first.c_str(), data_.second);
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2 )- (ImGui::CalcTextSize(buffer).x / 2));
ImGui::TextColored(ImVec4(1.0, 1.0, 1.0, alpha / params.background_alpha), "%s %.1f", data_.first.c_str(), data_.second);
}
float max = *max_element(benchmark.fps_data.begin(), benchmark.fps_data.end());
ImVec4 plotColor = HUDElements.colors.frametime;
plotColor.w = alpha / params.background_alpha;
ImGui::PushStyleColor(ImGuiCol_PlotLines, plotColor);
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0, 0.0, 0.0, alpha / params.background_alpha));
ImGui::Dummy(ImVec2(0.0f, 8.0f));
if (params.enabled[OVERLAY_PARAM_ENABLED_histogram])
ImGui::PlotHistogram("", benchmark.fps_data.data(), benchmark.fps_data.size(), 0, "", 0.0f, max + 10, ImVec2(ImGui::GetContentRegionAvailWidth(), 50));
else
ImGui::PlotLines("", benchmark.fps_data.data(), benchmark.fps_data.size(), 0, "", 0.0f, max + 10, ImVec2(ImGui::GetContentRegionAvailWidth(), 50));
ImGui::PopStyleColor(2);
ImGui::End();
}
ImVec4 change_on_load_temp(LOAD_DATA& data, unsigned current)
{
if (current >= data.high_load){
return data.color_high;
}
else if (current >= data.med_load){
float diff = float(current - data.med_load) / float(data.high_load - data.med_load);
float x = (data.color_high.x - data.color_med.x) * diff;
float y = (data.color_high.y - data.color_med.y) * diff;
float z = (data.color_high.z - data.color_med.z) * diff;
return ImVec4(data.color_med.x + x, data.color_med.y + y, data.color_med.z + z, 1.0);
} else {
float diff = float(current) / float(data.med_load);
float x = (data.color_med.x - data.color_low.x) * diff;
float y = (data.color_med.y - data.color_low.y) * diff;
float z = (data.color_med.z - data.color_low.z) * diff;
return ImVec4(data.color_low.x + x, data.color_low.y + y, data.color_low.z + z, 1.0);
}
}
void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2& window_size, bool is_vulkan)
{
HUDElements.sw_stats = &data; HUDElements.params = &params;
HUDElements.is_vulkan = is_vulkan;
ImGui::GetIO().FontGlobalScale = params.font_scale;
if(!logger) logger = std::make_unique<Logger>(&params);
static float ralign_width = 0, old_scale = 0;
window_size = ImVec2(params.width, params.height);
unsigned height = ImGui::GetIO().DisplaySize.y;
auto now = Clock::now();
if (old_scale != params.font_scale) {
HUDElements.ralign_width = ralign_width = ImGui::CalcTextSize("A").x * 4 /* characters */;
old_scale = params.font_scale;
}
if (!params.no_display){
ImGui::Begin("Main", &gui_open, ImGuiWindowFlags_NoDecoration);
2020-12-18 15:14:05 +00:00
ImGui::BeginTable("hud", params.table_columns, ImGuiTableFlags_NoClip);
HUDElements.place = 0;
for (auto& func : HUDElements.ordered_functions){
func.first();
HUDElements.place += 1;
}
ImGui::EndTable();
if(logger->is_active())
ImGui::GetWindowDrawList()->AddCircleFilled(ImVec2(data.main_window_pos.x + window_size.x - 15, data.main_window_pos.y + 15), 10, params.engine_color, 20);
window_size = ImVec2(window_size.x, ImGui::GetCursorPosY() + 10.0f);
ImGui::End();
if((now - logger->last_log_end()) < 12s)
render_benchmark(data, params, window_size, height, now);
}
}
void init_cpu_stats(overlay_params& params)
{
2021-10-01 20:34:21 +00:00
#ifdef __linux__
auto& enabled = params.enabled;
enabled[OVERLAY_PARAM_ENABLED_cpu_stats] = cpuStats.Init()
&& enabled[OVERLAY_PARAM_ENABLED_cpu_stats];
enabled[OVERLAY_PARAM_ENABLED_cpu_temp] = cpuStats.GetCpuFile()
&& enabled[OVERLAY_PARAM_ENABLED_cpu_temp];
enabled[OVERLAY_PARAM_ENABLED_cpu_power] = cpuStats.InitCpuPowerData()
&& enabled[OVERLAY_PARAM_ENABLED_cpu_power];
#endif
}
struct pci_bus {
int domain;
int bus;
int slot;
int func;
};
void init_gpu_stats(uint32_t& vendorID, uint32_t target_device_id, overlay_params& params)
{
//if (!params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats])
// return;
pci_bus pci;
bool pci_bus_parsed = false;
const char *pci_dev = nullptr;
if (!params.pci_dev.empty())
pci_dev = params.pci_dev.c_str();
// for now just checks if pci bus parses correctly, if at all necessary
if (pci_dev) {
if (sscanf(pci_dev, "%04x:%02x:%02x.%x",
&pci.domain, &pci.bus,
&pci.slot, &pci.func) == 4) {
pci_bus_parsed = true;
// reformat back to sysfs file name's and nvml's expected format
// so config file param's value format doesn't have to be as strict
std::stringstream ss;
ss << std::hex
<< std::setw(4) << std::setfill('0') << pci.domain << ":"
<< std::setw(2) << pci.bus << ":"
<< std::setw(2) << pci.slot << "."
<< std::setw(1) << pci.func;
params.pci_dev = ss.str();
pci_dev = params.pci_dev.c_str();
SPDLOG_DEBUG("PCI device ID: '{}'", pci_dev);
} else {
SPDLOG_ERROR("Failed to parse PCI device ID: '{}'", pci_dev);
SPDLOG_ERROR("Specify it as 'domain:bus:slot.func'");
}
}
// NVIDIA or Intel but maybe has Optimus
if (vendorID == 0x8086
|| vendorID == 0x10de) {
if(checkNvidia(pci_dev))
vendorID = 0x10de;
else
params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = false;
}
2021-10-01 20:34:21 +00:00
#ifdef __linux__
if (vendorID == 0x8086 || vendorID == 0x1002
|| gpu.find("Radeon") != std::string::npos
|| gpu.find("AMD") != std::string::npos) {
string path;
string drm = "/sys/class/drm/";
getAmdGpuInfo_actual = getAmdGpuInfo;
bool using_libdrm = false;
auto dirs = ls(drm.c_str(), "card");
for (auto& dir : dirs) {
path = drm + dir;
SPDLOG_DEBUG("device path check: {}/device/vendor", path);
2021-07-16 00:28:46 +00:00
string device = read_line(path + "/device/device");
deviceID = strtol(device.c_str(), NULL, 16); // OGL might fail so read from sysfs
string vendor = read_line(path + "/device/vendor");
trim(vendor);
if (vendor != "0x1002")
continue;
if (pci_bus_parsed && pci_dev) {
string pci_device = read_symlink((path + "/device").c_str());
SPDLOG_DEBUG("PCI device symlink: '{}'", pci_device);
if (!ends_with(pci_device, pci_dev)) {
SPDLOG_WARN("skipping GPU, PCI ID doesn't match: {}", pci_device);
continue;
}
}
// Don't skip if intel, maybe there's a dgpu
else if (vendorID != 0x8086 && target_device_id && target_device_id != deviceID)
{
SPDLOG_WARN("expected device id {:04X}, got {:04X}, skipping", target_device_id, deviceID);
continue;
}
string module = get_basename(read_symlink(path + "/device/driver/module"));
SPDLOG_DEBUG("using device path: '{}', module: '{}'", path, module);
int idx = -1;
//TODO make neater
int res = sscanf(path.c_str(), "%*[^0-9]%d", &idx);
(void)res;
std::string dri_path = "/dev/dri/card" + std::to_string(idx);
if (module == "amdgpu")
{
#ifdef HAVE_LIBDRM_AMDGPU
if (!params.enabled[OVERLAY_PARAM_ENABLED_force_amdgpu_hwmon] && res == 1 && amdgpu_open(dri_path.c_str())) {
vendorID = 0x1002;
using_libdrm = true;
getAmdGpuInfo_actual = getAmdGpuInfo_libdrm;
amdgpu_set_sampling_period(params.fps_sampling_period);
SPDLOG_DEBUG("Using libdrm");
// fall through and open sysfs handles for fallback or check DRM version beforehand
} else if (!params.enabled[OVERLAY_PARAM_ENABLED_force_amdgpu_hwmon]) {
SPDLOG_WARN("Failed to open device '/dev/dri/card{}' with libdrm, falling back to using hwmon sysfs.", idx);
}
#endif
}
#ifdef HAVE_LIBDRM
else if (module == "radeon")
{
if (res == 1 && radeon_open(dri_path.c_str())) {
vendorID = 0x1002;
using_libdrm = true;
getAmdGpuInfo_actual = getRadeonInfo_libdrm;
radeon_set_sampling_period(params.fps_sampling_period);
} else {
SPDLOG_WARN("Failed to open device '/dev/dri/card{}' with libdrm.", idx);
params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = false;
break;
}
}
#endif
if (!file_exists(path + "/device/gpu_busy_percent"))
continue;
path += "/device";
if (!amdgpu.busy)
amdgpu.busy = fopen((path + "/gpu_busy_percent").c_str(), "r");
if (!amdgpu.vram_total)
amdgpu.vram_total = fopen((path + "/mem_info_vram_total").c_str(), "r");
if (!amdgpu.vram_used)
amdgpu.vram_used = fopen((path + "/mem_info_vram_used").c_str(), "r");
path += "/hwmon/";
string tempFolder;
if (find_folder(path, "hwmon", tempFolder)) {
if (!amdgpu.core_clock)
amdgpu.core_clock = fopen((path + tempFolder + "/freq1_input").c_str(), "r");
if (!amdgpu.memory_clock)
amdgpu.memory_clock = fopen((path + tempFolder + "/freq2_input").c_str(), "r");
if (!amdgpu.temp)
amdgpu.temp = fopen((path + tempFolder + "/temp1_input").c_str(), "r");
if (!amdgpu.power_usage)
amdgpu.power_usage = fopen((path + tempFolder + "/power1_average").c_str(), "r");
vendorID = 0x1002;
break;
}
}
// don't bother then
if (!using_libdrm && !amdgpu.busy && !amdgpu.temp && !amdgpu.vram_total && !amdgpu.vram_used) {
params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = false;
}
}
#endif
if (!params.permit_upload)
SPDLOG_INFO("Uploading is disabled (permit_upload = 0)");
}
void init_system_info(){
2021-10-01 20:34:21 +00:00
#ifdef __linux__
const char* ld_preload = getenv("LD_PRELOAD");
if (ld_preload)
unsetenv("LD_PRELOAD");
ram = exec("cat /proc/meminfo | grep 'MemTotal' | awk '{print $2}'");
trim(ram);
cpu = exec("cat /proc/cpuinfo | grep 'model name' | tail -n1 | sed 's/^.*: //' | sed 's/([^)]*)/()/g' | tr -d '(/)'");
trim(cpu);
kernel = exec("uname -r");
trim(kernel);
os = exec("cat /etc/*-release | grep 'PRETTY_NAME' | cut -d '=' -f 2-");
os.erase(remove(os.begin(), os.end(), '\"' ), os.end());
trim(os);
cpusched = read_line("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor");
const char* mangohud_recursion = getenv("MANGOHUD_RECURSION");
if (!mangohud_recursion) {
setenv("MANGOHUD_RECURSION", "1", 1);
driver = exec("glxinfo -B | grep 'OpenGL version' | sed 's/^.*: //' | sed 's/([^()]*)//g' | tr -s ' '");
trim(driver);
unsetenv("MANGOHUD_RECURSION");
} else {
driver = "MangoHud glxinfo recursion detected";
}
// Get WINE version
wineProcess = get_exe_path();
auto n = wineProcess.find_last_of('/');
string preloader = wineProcess.substr(n + 1);
if (preloader == "wine-preloader" || preloader == "wine64-preloader") {
// Check if using Proton
if (wineProcess.find("/dist/bin/wine") != std::string::npos || wineProcess.find("/files/bin/wine") != std::string::npos) {
stringstream ss;
ss << dirname((char*)wineProcess.c_str()) << "/../../version";
string protonVersion = ss.str();
ss.str(""); ss.clear();
ss << read_line(protonVersion);
std::getline(ss, wineVersion, ' '); // skip first number string
std::getline(ss, wineVersion, ' ');
trim(wineVersion);
string toReplace = "proton-";
size_t pos = wineVersion.find(toReplace);
if (pos != std::string::npos) {
// If found replace
wineVersion.replace(pos, toReplace.length(), "Proton ");
}
else {
// If not found insert for non official proton builds
wineVersion.insert(0, "Proton ");
}
}
else {
char *dir = dirname((char*)wineProcess.c_str());
stringstream findVersion;
findVersion << "\"" << dir << "/wine\" --version";
const char *wine_env = getenv("WINELOADERNOEXEC");
if (wine_env)
unsetenv("WINELOADERNOEXEC");
wineVersion = exec(findVersion.str());
std::cout << "WINE VERSION = " << wineVersion << "\n";
if (wine_env)
setenv("WINELOADERNOEXEC", wine_env, 1);
}
}
else {
wineVersion = "";
}
// check for gamemode and vkbasalt
fs::path path("/proc/self/map_files/");
for (auto& p : fs::directory_iterator(path)) {
auto filename = p.path().string();
auto sym = read_symlink(filename.c_str());
if (sym.find("gamemode") != std::string::npos)
HUDElements.gamemode_bol = true;
if (sym.find("vkbasalt") != std::string::npos)
HUDElements.vkbasalt_bol = true;
if (HUDElements.gamemode_bol && HUDElements.vkbasalt_bol)
break;
}
if (ld_preload)
setenv("LD_PRELOAD", ld_preload, 1);
2021-07-16 00:28:46 +00:00
SPDLOG_DEBUG("Ram:{}", ram);
SPDLOG_DEBUG("Cpu:{}", cpu);
SPDLOG_DEBUG("Kernel:{}", kernel);
SPDLOG_DEBUG("Os:{}", os);
SPDLOG_DEBUG("Gpu:{}", gpu);
SPDLOG_DEBUG("Driver:{}", driver);
SPDLOG_DEBUG("CPU Scheduler:{}", cpusched);
#endif
}
void get_device_name(int32_t vendorID, int32_t deviceID, struct swapchain_stats& sw_stats)
{
2021-10-01 20:34:21 +00:00
#ifdef __linux__
2021-08-12 15:13:48 +00:00
if (pci_ids.find(vendorID) == pci_ids.end())
parse_pciids();
string desc = pci_ids[vendorID].second[deviceID].desc;
size_t position = desc.find("[");
if (position != std::string::npos) {
desc = desc.substr(position);
string chars = "[]";
for (char c: chars)
desc.erase(remove(desc.begin(), desc.end(), c), desc.end());
}
gpu = sw_stats.gpuName = desc;
trim(sw_stats.gpuName); trim(gpu);
#endif
}