diff --git a/src/gpu.cpp b/src/gpu.cpp index d88d29d0..a420d119 100644 --- a/src/gpu.cpp +++ b/src/gpu.cpp @@ -190,5 +190,17 @@ void getAmdGpuInfo(){ value = 0; gpu_info.voltage = value; } + + if (amdgpu.fdinfo){ + rewind(amdgpu.fdinfo); + fflush(amdgpu.fdinfo); + char line[256]; + while (fgets(line, sizeof(line), amdgpu.fdinfo)) + if(strstr(line, "drm-engine-gfx")) + sscanf(line, "drm-engine-gfx: %" SCNu64 " ns", &value); + + gpu_info.app_busy_ns = value; + } + #endif } diff --git a/src/gpu.h b/src/gpu.h index 58753032..33eb7c55 100644 --- a/src/gpu.h +++ b/src/gpu.h @@ -21,6 +21,7 @@ struct amdgpu_files FILE *gtt_used; FILE *fan; FILE *gpu_voltage_soc; + FILE *fdinfo; }; extern amdgpu_files amdgpu; @@ -44,6 +45,7 @@ struct gpuInfo{ float gtt_used; int fan_speed; int voltage; + uint64_t app_busy_ns; }; extern struct gpuInfo gpu_info; diff --git a/src/hud_elements.cpp b/src/hud_elements.cpp index c05fbfa2..16c1ebb0 100644 --- a/src/hud_elements.cpp +++ b/src/hud_elements.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "overlay.h" #include "overlay_params.h" #include "hud_elements.h" @@ -271,6 +272,17 @@ void HudElements::gpu_stats(){ HUDElements.TextColored(HUDElements.colors.text, "mV"); ImGui::PopFont(); } + + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_ms]){ + ImguiNextColumnOrNewRow(); + double sum = std::accumulate(HUDElements.gputimes_data.begin(), HUDElements.gputimes_data.end(), 0.000); + double gputimes_avg = sum / HUDElements.gputimes_data.size(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.1f", gputimes_avg); + ImGui::SameLine(0, 1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + HUDElements.TextColored(HUDElements.colors.text, "ms"); + ImGui::PopFont(); + } } } @@ -345,6 +357,17 @@ void HudElements::cpu_stats(){ HUDElements.TextColored(HUDElements.colors.text, "W"); ImGui::PopFont(); } + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_cpu_ms]){ + ImguiNextColumnOrNewRow(); + double sum = std::accumulate(HUDElements.cputimes_data.begin(), HUDElements.cputimes_data.end(), 0.000); + double cputimes_avg = sum / HUDElements.cputimes_data.size(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.1f", cputimes_avg); + ImGui::SameLine(0, 1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + HUDElements.TextColored(HUDElements.colors.text, "ms"); + ImGui::PopFont(); + } + } } diff --git a/src/hud_elements.h b/src/hud_elements.h index 677e892e..0d94b111 100644 --- a/src/hud_elements.h +++ b/src/hud_elements.h @@ -29,6 +29,8 @@ class HudElements{ std::vector> ordered_functions; std::vector gamescope_debug_latency {}; std::vector gamescope_debug_app {}; + std::vector cputimes_data; + std::vector gputimes_data; int min, max, gpu_core_max, gpu_mem_max, cpu_temp_max, gpu_temp_max; const std::vector permitted_params = { "gpu_load", "cpu_load", "gpu_core_clock", "gpu_mem_clock", diff --git a/src/overlay.cpp b/src/overlay.cpp index e7a58296..bcfd00b9 100644 --- a/src/overlay.cpp +++ b/src/overlay.cpp @@ -892,6 +892,15 @@ void init_gpu_stats(uint32_t& vendorID, uint32_t reported_deviceID, overlay_para } } + for (const auto& file : fs::directory_iterator("/proc/self/fdinfo")){ + std::ifstream f(file.path().string()); + std::string line; + if (f) + while (std::getline(f, line)) + if (line.find("amdgpu") != std::string::npos) + amdgpu.fdinfo = fopen(file.path().string().c_str(), "r"); + } + if (!metrics_path.empty()) break; diff --git a/src/overlay_params.h b/src/overlay_params.h index 564af21f..e19e0fd8 100644 --- a/src/overlay_params.h +++ b/src/overlay_params.h @@ -104,6 +104,8 @@ typedef unsigned long KeySym; OVERLAY_PARAM_BOOL(temp_fahrenheit) \ OVERLAY_PARAM_BOOL(dynamic_frame_timing) \ OVERLAY_PARAM_BOOL(duration) \ + OVERLAY_PARAM_BOOL(cpu_ms) \ + OVERLAY_PARAM_BOOL(gpu_ms) \ OVERLAY_PARAM_CUSTOM(fps_sampling_period) \ OVERLAY_PARAM_CUSTOM(output_folder) \ OVERLAY_PARAM_CUSTOM(output_file) \ diff --git a/src/vulkan.cpp b/src/vulkan.cpp index 1c8eff12..2f369296 100644 --- a/src/vulkan.cpp +++ b/src/vulkan.cpp @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include "mesa/util/macros.h" // defines "restrict" for vk_util.h #include "mesa/util/os_socket.h" @@ -50,6 +52,7 @@ #include "notify.h" #include "blacklist.h" #include "pci_ids.h" +#include "gpu.h" using namespace std; @@ -1571,10 +1574,50 @@ static void overlay_DestroySwapchainKHR( destroy_swapchain_data(swapchain_data); } +static unsigned long long getProcessCpuTimeTicks() { + std::ifstream stat_file("/proc/self/stat"); + std::string stat_data; + + if (!stat_file) { + std::cerr << "Failed to open /proc/self/stat" << std::endl; + return 0; + } + + getline(stat_file, stat_data); + stat_file.close(); + + unsigned long long utime, stime; + std::istringstream iss(stat_data); + for (int i = 1; i <= 13; ++i) { + iss.ignore(std::numeric_limits::max(), ' '); + } + iss >> utime >> stime; // The 14th and 15th fields in /proc/self/stat + + return utime + stime; +} + +static uint64_t getGpuTime() { + rewind(amdgpu.fdinfo); + fflush(amdgpu.fdinfo); + char line[256]; + uint64_t val; + while (fgets(line, sizeof(line), amdgpu.fdinfo)) + if(strstr(line, "drm-engine-gfx")) + sscanf(line, "drm-engine-gfx: %" SCNu64 " ns", &val); + + return val; +} + static VkResult overlay_QueuePresentKHR( VkQueue queue, const VkPresentInfoKHR* pPresentInfo) { + unsigned long long startCpuTime = getProcessCpuTimeTicks(); + static bool gpu_keep_last = false; + static uint64_t startGpuTime; + if (!gpu_keep_last) + startGpuTime = getGpuTime(); + using namespace std::chrono_literals; if (fps_limit_stats.targetFrameTime > 0s && fps_limit_stats.method == FPS_LIMIT_METHOD_EARLY){ fps_limit_stats.frameStart = Clock::now(); @@ -1629,6 +1672,26 @@ static VkResult overlay_QueuePresentKHR( FpsLimiter(fps_limit_stats); fps_limit_stats.frameEnd = Clock::now(); } + // Calculate the GPU time used for this frame in milliseconds + uint64_t endGpuTime = getGpuTime(); + if (endGpuTime > startGpuTime){ + double gpuTime = static_cast(endGpuTime - startGpuTime) / 1000000.0; + HUDElements.gputimes_data.push_back(gpuTime); + if (HUDElements.gputimes_data.size() > 199) + HUDElements.gputimes_data.erase(HUDElements.gputimes_data.begin()); + + gpu_keep_last = false; + } else { + gpu_keep_last = true; + } + + // Calculate the CPU time used for this frame in milliseconds + unsigned long long endCpuTime = getProcessCpuTimeTicks(); + unsigned long long cpuTimeTicks = endCpuTime - startCpuTime; + double cpuTime = static_cast(cpuTimeTicks) * 1000.0 / sysconf(_SC_CLK_TCK); + HUDElements.cputimes_data.push_back(cpuTime); + if (HUDElements.cputimes_data.size() > 199) + HUDElements.cputimes_data.erase(HUDElements.cputimes_data.begin()); return result; }