--- a/src/Cafe/HW/Latte/Core/LatteOverlay.cpp +++ b/src/Cafe/HW/Latte/Core/LatteOverlay.cpp @@ -12,30 +12,16 @@ #include "imgui/imgui_extension.h" #include "input/InputManager.h" +#include "util/SystemInfo/SystemInfo.h" #include -#if BOOST_OS_WINDOWS -#include -#include -#pragma comment(lib, "ntdll.lib") -#endif - struct OverlayStats { OverlayStats() {}; int processor_count = 1; - - // cemu cpu stats - uint64_t last_cpu{}, kernel{}, user{}; - - // global cpu stats - struct ProcessorTime - { - uint64_t idle{}, kernel{}, user{}; - }; - + ProcessorTime processor_time_cemu; std::vector processor_times; double fps{}; @@ -565,83 +551,52 @@ void LatteOverlay_render(bool pad_view) } } - void LatteOverlay_init() { -#if BOOST_OS_WINDOWS - SYSTEM_INFO sys_info; - GetSystemInfo(&sys_info); - g_state.processor_count = sys_info.dwNumberOfProcessors; + g_state.processor_count = GetProcessorCount(); g_state.processor_times.resize(g_state.processor_count); g_state.cpu_per_core.resize(g_state.processor_count); -#else - g_state.processor_count = 1; -#endif } -void LatteOverlay_updateStats(double fps, sint32 drawcalls) +static void UpdateStats_CemuCpu() { - if (GetConfig().overlay.position == ScreenPosition::kDisabled) - return; + ProcessorTime now; + QueryProcTime(now); + + double cpu = ProcessorTime::Compare(g_state.processor_time_cemu, now); + cpu /= g_state.processor_count; + + g_state.cpu_usage = cpu * 100; + g_state.processor_time_cemu = now; +} - g_state.fps = fps; - g_state.draw_calls_per_frame = drawcalls; +static void UpdateStats_CpuPerCore() +{ + std::vector now(g_state.processor_count); + QueryCoreTimes(g_state.processor_count, now.data()); -#if BOOST_OS_WINDOWS - // update cemu cpu - FILETIME ftime, fkernel, fuser; - LARGE_INTEGER now, kernel, user; - GetSystemTimeAsFileTime(&ftime); - now.LowPart = ftime.dwLowDateTime; - now.HighPart = ftime.dwHighDateTime; - - GetProcessTimes(GetCurrentProcess(), &ftime, &ftime, &fkernel, &fuser); - kernel.LowPart = fkernel.dwLowDateTime; - kernel.HighPart = fkernel.dwHighDateTime; - - user.LowPart = fuser.dwLowDateTime; - user.HighPart = fuser.dwHighDateTime; - - double percent = (kernel.QuadPart - g_state.kernel) + (user.QuadPart - g_state.user); - percent /= (now.QuadPart - g_state.last_cpu); - percent /= g_state.processor_count; - g_state.cpu_usage = percent * 100; - g_state.last_cpu = now.QuadPart; - g_state.user = user.QuadPart; - g_state.kernel = kernel.QuadPart; - - // update cpu per core - std::vector sppi(g_state.processor_count); - if (NT_SUCCESS(NtQuerySystemInformation(SystemProcessorPerformanceInformation, sppi.data(), sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * g_state.processor_count, nullptr))) + for (int32_t i = 0; i < g_state.processor_count; ++i) { - for (sint32 i = 0; i < g_state.processor_count; ++i) - { - const uint64 kernel_diff = sppi[i].KernelTime.QuadPart - g_state.processor_times[i].kernel; - const uint64 user_diff = sppi[i].UserTime.QuadPart - g_state.processor_times[i].user; - const uint64 idle_diff = sppi[i].IdleTime.QuadPart - g_state.processor_times[i].idle; - - const auto total = kernel_diff + user_diff; // kernel time already includes idletime - const double cpu = total == 0 ? 0 : (1.0 - ((double)idle_diff / total)) * 100.0; + double cpu = ProcessorTime::Compare(g_state.processor_times[i], now[i]); - g_state.cpu_per_core[i] = cpu; - //total_cpu += cpu; + g_state.cpu_per_core[i] = cpu * 100; + g_state.processor_times[i] = now[i]; + } +} - g_state.processor_times[i].idle = sppi[i].IdleTime.QuadPart; - g_state.processor_times[i].kernel = sppi[i].KernelTime.QuadPart; - g_state.processor_times[i].user = sppi[i].UserTime.QuadPart; - } +void LatteOverlay_updateStats(double fps, sint32 drawcalls) +{ + if (GetConfig().overlay.position == ScreenPosition::kDisabled) + return; - //total_cpu /= g_state.processor_count; - //g_state.cpu_usage = total_cpu; - } + g_state.fps = fps; + g_state.draw_calls_per_frame = drawcalls; + UpdateStats_CemuCpu(); + UpdateStats_CpuPerCore(); // update ram - PROCESS_MEMORY_COUNTERS pmc{}; - pmc.cb = sizeof(pmc); - GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)); - g_state.ram_usage = (pmc.WorkingSetSize / 1000) / 1000; -#endif + g_state.ram_usage = (QueryRamUsage() / 1000) / 1000; // update vram g_renderer->GetVRAMInfo(g_state.vramUsage, g_state.vramTotal); --- /dev/null +++ b/src/util/SystemInfo/SystemInfo.cpp @@ -0,0 +1,34 @@ +#include "util/SystemInfo/SystemInfo.h" + +uint64 ProcessorTime::work() +{ + return user + kernel; +} + +uint64 ProcessorTime::total() +{ + return idle + user + kernel; +} + +double ProcessorTime::Compare(ProcessorTime &last, ProcessorTime &now) +{ + auto dwork = now.work() - last.work(); + auto dtotal = now.total() - last.total(); + + return (double)dwork / dtotal; +} + +uint32 GetProcessorCount() +{ + return std::thread::hardware_concurrency(); +} + +void QueryProcTime(ProcessorTime &out) +{ + uint64 now, user, kernel; + QueryProcTime(now, user, kernel); + + out.idle = now - (user + kernel); + out.kernel = kernel; + out.user = user; +} \ No newline at end of file --- /dev/null +++ b/src/util/SystemInfo/SystemInfo.h @@ -0,0 +1,17 @@ +#pragma once + +struct ProcessorTime +{ + uint64 idle{}, kernel{}, user{}; + + uint64 work(); + uint64 total(); + + static double Compare(ProcessorTime &last, ProcessorTime &now); +}; + +uint32 GetProcessorCount(); +uint64 QueryRamUsage(); +void QueryProcTime(uint64 &out_now, uint64 &out_user, uint64 &out_kernel); +void QueryProcTime(ProcessorTime &out); +void QueryCoreTimes(uint32 count, ProcessorTime out[]); \ No newline at end of file --- /dev/null +++ b/src/util/SystemInfo/SystemInfoLinux.cpp @@ -0,0 +1,67 @@ +#if BOOST_OS_LINUX + +#include "util/SystemInfo/SystemInfo.h" + +#include +#include + +uint64 QueryRamUsage() +{ + long page_size = sysconf(_SC_PAGESIZE); + if (page_size == -1) + { + return 0; + } + + std::ifstream file("/proc/self/statm"); + if (file) + { + file.ignore(std::numeric_limits::max(), ' '); + uint64 no_pages; + file >> no_pages; + + return no_pages * page_size; + } + else + { + return 0; + } +} + +void QueryProcTime(uint64 &out_now, uint64 &out_user, uint64 &out_kernel) +{ + struct tms time_info; + clock_t clock_now = times(&time_info); + clock_t clock_user = time_info.tms_utime; + clock_t clock_kernel = time_info.tms_stime; + out_now = static_cast(clock_now); + out_user = static_cast(clock_user); + out_kernel = static_cast(clock_kernel); +} + +void QueryCoreTimes(uint32 count, ProcessorTime out[]) +{ + std::ifstream file("/proc/stat"); + if (file) + { + file.ignore(std::numeric_limits::max(), '\n'); + + for (auto i = 0; i < count; ++i) + { + uint64 user, nice, kernel, idle; + file.ignore(std::numeric_limits::max(), ' '); + file >> user >> nice >> kernel >> idle; + file.ignore(std::numeric_limits::max(), '\n'); + + out[i].idle = idle; + out[i].kernel = kernel; + out[i].user = user + nice; + } + } + else + { + for (auto i = 0; i < count; ++i) out[i] = { }; + } +} + +#endif \ No newline at end of file --- /dev/null +++ b/src/util/SystemInfo/SystemInfoStub.cpp @@ -0,0 +1,25 @@ +#if !BOOST_OS_WINDOWS && !BOOST_OS_LINUX + +#include "util/SystemInfo/SystemInfo.h" + +uint64 QueryRamUsage() +{ + return 0; +} + +void QueryProcTime(uint64 &out_now, uint64 &out_user, uint64 &out_kernel) +{ + out_now = 0; + out_user = 0; + out_kernel = 0; +} + +void QueryCoreTimes(uint32 count, ProcessorTime out[]) +{ + for (auto i = 0; i < count; ++i) + { + out[i] = { }; + } +} + +#endif \ No newline at end of file --- /dev/null +++ b/src/util/SystemInfo/SystemInfoWin.cpp @@ -0,0 +1,72 @@ +#if BOOST_OS_WINDOWS + +#include "util/SystemInfo/SystemInfo.h" + +#include +#include +#pragma comment(lib, "ntdll.lib") + +uint64 QueryRamUsage() +{ + PROCESS_MEMORY_COUNTERS pmc{}; + pmc.cb = sizeof(pmc); + if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) + { + return pmc.WorkingSetSize; + } + else + { + return 0; + } +} + +void QueryProcTime(uint64 &out_now, uint64 &out_user, uint64 &out_kernel) +{ + FILETIME ftime, fkernel, fuser; + LARGE_INTEGER now, kernel, user; + GetSystemTimeAsFileTime(&ftime); + now.LowPart = ftime.dwLowDateTime; + now.HighPart = ftime.dwHighDateTime; + + if (GetProcessTimes(GetCurrentProcess(), &ftime, &ftime, &fkernel, &fuser)) + { + kernel.LowPart = fkernel.dwLowDateTime; + kernel.HighPart = fkernel.dwHighDateTime; + + user.LowPart = fuser.dwLowDateTime; + user.HighPart = fuser.dwHighDateTime; + + out_now = now.QuadPart; + out_user = user.QuadPart; + out_kernel = kernel.QuadPart; + } + else + { + out_now = 0; + out_user = 0; + out_kernel = 0; + } +} + +void QueryCoreTimes(uint32 count, ProcessorTime out[]) +{ + std::vector sppi(count); + if (NT_SUCCESS(NtQuerySystemInformation(SystemProcessorPerformanceInformation, sppi.data(), sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * count, nullptr))) + { + for (auto i = 0; i < count; ++i) + { + out[i].idle = sppi[i].IdleTime.QuadPart; + out[i].kernel = sppi[i].KernelTime.QuadPart; + out[i].user = sppi[i].UserTime.QuadPart; + } + } + else + { + for (auto i = 0; i < count; ++i) + { + out[i] = { }; + } + } +} + +#endif \ No newline at end of file