Skip to content

Commit

Permalink
Linux/macOS: Implement overlay CPU/memory statistics (#480)
Browse files Browse the repository at this point in the history
  • Loading branch information
goeiecool9999 committed Nov 20, 2022
1 parent c170973 commit 9ce52ad
Show file tree
Hide file tree
Showing 14 changed files with 322 additions and 98 deletions.
109 changes: 32 additions & 77 deletions src/Cafe/HW/Latte/Core/LatteOverlay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,16 @@
#include "imgui/imgui_extension.h"

#include "input/InputManager.h"
#include "util/SystemInfo/SystemInfo.h"

#include <cinttypes>

#if BOOST_OS_WINDOWS
#include <Psapi.h>
#include <winternl.h>
#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<ProcessorTime> processor_times;

double fps{};
Expand Down Expand Up @@ -562,83 +548,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<ProcessorTime> now(g_state.processor_count);
QueryCoreTimes(g_state.processor_count, now);

#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<SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION> 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);
Expand Down
9 changes: 5 additions & 4 deletions src/Cafe/HW/Latte/Core/LattePerformanceMonitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ void LattePerformanceMonitor_frameEnd()
performanceMonitor.cycle[nextCycleIndex].recompilerLeaveCount = 0;
performanceMonitor.cycle[nextCycleIndex].threadLeaveCount = 0;
performanceMonitor.cycleIndex = nextCycleIndex;


// next update in 1 second
performanceMonitor.cycle[performanceMonitor.cycleIndex].lastUpdate = GetTickCount();

if (isFirstUpdate)
{
LatteOverlay_updateStats(0.0, 0);
Expand All @@ -109,8 +112,6 @@ void LattePerformanceMonitor_frameEnd()
LatteOverlay_updateStats(fps, drawCallCounter / elapsedFrames);
gui_updateWindowTitles(false, false, fps);
}
// next update in 1 second
performanceMonitor.cycle[performanceMonitor.cycleIndex].lastUpdate = GetTickCount();

// prevent hibernation and screen saver/monitor off
#if BOOST_OS_WINDOWS
Expand All @@ -124,4 +125,4 @@ void LattePerformanceMonitor_frameBegin()
{
performanceMonitor.vk.numDrawBarriersPerFrame.reset();
performanceMonitor.vk.numBeginRenderpassPerFrame.reset();
}
}
23 changes: 19 additions & 4 deletions src/util/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ add_library(CemuUtil
DXGIWrapper/DXGIWrapper.h
EventService.h
Fiber/Fiber.h
Fiber/FiberUnix.cpp
Fiber/FiberWin.cpp
helpers/ClassWrapper.h
helpers/ConcurrentQueue.h
helpers/enum_array.hpp
Expand Down Expand Up @@ -50,8 +48,8 @@ add_library(CemuUtil
math/vector2.h
math/vector3.h
MemMapper/MemMapper.h
MemMapper/MemMapperUnix.cpp
MemMapper/MemMapperWin.cpp
SystemInfo/SystemInfo.cpp
SystemInfo/SystemInfo.h
ThreadPool/ThreadPool.cpp
ThreadPool/ThreadPool.h
tinyxml2/tinyxml2.cpp
Expand All @@ -71,6 +69,23 @@ add_library(CemuUtil
Zir/Passes/ZpIRRegisterAllocator.cpp
)

if(WIN32)
target_sources(CemuUtil PRIVATE Fiber/FiberWin.cpp)
target_sources(CemuUtil PRIVATE MemMapper/MemMapperWin.cpp)
target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoWin.cpp)
elseif(UNIX)
target_sources(CemuUtil PRIVATE Fiber/FiberUnix.cpp)
target_sources(CemuUtil PRIVATE MemMapper/MemMapperUnix.cpp)
target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoUnix.cpp)
if(NOT APPLE)
target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoLinux.cpp)
else()
target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoMac.cpp)
endif()
else()
target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoStub.cpp)
endif()

set_property(TARGET CemuUtil PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")

target_include_directories(CemuUtil PUBLIC "../")
Expand Down
3 changes: 0 additions & 3 deletions src/util/Fiber/FiberUnix.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include "Fiber.h"
#if BOOST_OS_LINUX || BOOST_OS_MACOS
#include <ucontext.h>

thread_local Fiber* sCurrentFiber{};
Expand Down Expand Up @@ -52,5 +51,3 @@ void* Fiber::GetFiberPrivateData()
{
return sCurrentFiber->m_privateData;
}

#endif
3 changes: 0 additions & 3 deletions src/util/Fiber/FiberWin.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include "Fiber.h"
#if BOOST_OS_WINDOWS
#include <Windows.h>

thread_local Fiber* sCurrentFiber{};
Expand Down Expand Up @@ -39,5 +38,3 @@ void* Fiber::GetFiberPrivateData()
{
return sCurrentFiber->m_privateData;
}

#endif
3 changes: 0 additions & 3 deletions src/util/MemMapper/MemMapperUnix.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include "util/MemMapper/MemMapper.h"

#if BOOST_OS_LINUX || BOOST_OS_MACOS
#include <unistd.h>
#include <sys/mman.h>

Expand Down Expand Up @@ -65,5 +64,3 @@ namespace MemMapper
}

};

#endif
4 changes: 0 additions & 4 deletions src/util/MemMapper/MemMapperWin.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#include "util/MemMapper/MemMapper.h"

#if BOOST_OS_WINDOWS

#include <Windows.h>

namespace MemMapper
Expand Down Expand Up @@ -63,5 +61,3 @@ namespace MemMapper
}

};

#endif
34 changes: 34 additions & 0 deletions src/util/SystemInfo/SystemInfo.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
17 changes: 17 additions & 0 deletions src/util/SystemInfo/SystemInfo.h
Original file line number Diff line number Diff line change
@@ -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, std::vector<ProcessorTime>& out);
48 changes: 48 additions & 0 deletions src/util/SystemInfo/SystemInfoLinux.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include "util/SystemInfo/SystemInfo.h"

#include <unistd.h>

uint64 QueryRamUsage()
{
static 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<std::streamsize>::max(), ' ');
uint64 pages;
file >> pages;

return pages * page_size;
}
return 0;
}

void QueryCoreTimes(uint32 count, std::vector<ProcessorTime>& out)
{
std::ifstream file("/proc/stat");
if (file)
{
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

for (auto i = 0; i < out.size(); ++i)
{
uint64 user, nice, kernel, idle;
file.ignore(std::numeric_limits<std::streamsize>::max(), ' ');
file >> user >> nice >> kernel >> idle;
file.ignore(std::numeric_limits<std::streamsize>::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] = { };
}
}

0 comments on commit 9ce52ad

Please sign in to comment.