Skip to content

Commit 9ce52ad

Browse files
Linux/macOS: Implement overlay CPU/memory statistics (#480)
1 parent c170973 commit 9ce52ad

14 files changed

+322
-98
lines changed

src/Cafe/HW/Latte/Core/LatteOverlay.cpp

Lines changed: 32 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,16 @@
1212
#include "imgui/imgui_extension.h"
1313

1414
#include "input/InputManager.h"
15+
#include "util/SystemInfo/SystemInfo.h"
1516

1617
#include <cinttypes>
1718

18-
#if BOOST_OS_WINDOWS
19-
#include <Psapi.h>
20-
#include <winternl.h>
21-
#pragma comment(lib, "ntdll.lib")
22-
#endif
23-
2419
struct OverlayStats
2520
{
2621
OverlayStats() {};
2722

2823
int processor_count = 1;
29-
30-
// cemu cpu stats
31-
uint64_t last_cpu{}, kernel{}, user{};
32-
33-
// global cpu stats
34-
struct ProcessorTime
35-
{
36-
uint64_t idle{}, kernel{}, user{};
37-
};
38-
24+
ProcessorTime processor_time_cemu;
3925
std::vector<ProcessorTime> processor_times;
4026

4127
double fps{};
@@ -562,83 +548,52 @@ void LatteOverlay_render(bool pad_view)
562548
}
563549
}
564550

565-
566551
void LatteOverlay_init()
567552
{
568-
#if BOOST_OS_WINDOWS
569-
SYSTEM_INFO sys_info;
570-
GetSystemInfo(&sys_info);
571-
g_state.processor_count = sys_info.dwNumberOfProcessors;
553+
g_state.processor_count = GetProcessorCount();
572554

573555
g_state.processor_times.resize(g_state.processor_count);
574556
g_state.cpu_per_core.resize(g_state.processor_count);
575-
#else
576-
g_state.processor_count = 1;
577-
#endif
578557
}
579558

580-
void LatteOverlay_updateStats(double fps, sint32 drawcalls)
559+
static void UpdateStats_CemuCpu()
581560
{
582-
if (GetConfig().overlay.position == ScreenPosition::kDisabled)
583-
return;
561+
ProcessorTime now;
562+
QueryProcTime(now);
563+
564+
double cpu = ProcessorTime::Compare(g_state.processor_time_cemu, now);
565+
cpu /= g_state.processor_count;
566+
567+
g_state.cpu_usage = cpu * 100;
568+
g_state.processor_time_cemu = now;
569+
}
584570

585-
g_state.fps = fps;
586-
g_state.draw_calls_per_frame = drawcalls;
571+
static void UpdateStats_CpuPerCore()
572+
{
573+
std::vector<ProcessorTime> now(g_state.processor_count);
574+
QueryCoreTimes(g_state.processor_count, now);
587575

588-
#if BOOST_OS_WINDOWS
589-
// update cemu cpu
590-
FILETIME ftime, fkernel, fuser;
591-
LARGE_INTEGER now, kernel, user;
592-
GetSystemTimeAsFileTime(&ftime);
593-
now.LowPart = ftime.dwLowDateTime;
594-
now.HighPart = ftime.dwHighDateTime;
595-
596-
GetProcessTimes(GetCurrentProcess(), &ftime, &ftime, &fkernel, &fuser);
597-
kernel.LowPart = fkernel.dwLowDateTime;
598-
kernel.HighPart = fkernel.dwHighDateTime;
599-
600-
user.LowPart = fuser.dwLowDateTime;
601-
user.HighPart = fuser.dwHighDateTime;
602-
603-
double percent = (kernel.QuadPart - g_state.kernel) + (user.QuadPart - g_state.user);
604-
percent /= (now.QuadPart - g_state.last_cpu);
605-
percent /= g_state.processor_count;
606-
g_state.cpu_usage = percent * 100;
607-
g_state.last_cpu = now.QuadPart;
608-
g_state.user = user.QuadPart;
609-
g_state.kernel = kernel.QuadPart;
610-
611-
// update cpu per core
612-
std::vector<SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION> sppi(g_state.processor_count);
613-
if (NT_SUCCESS(NtQuerySystemInformation(SystemProcessorPerformanceInformation, sppi.data(), sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * g_state.processor_count, nullptr)))
576+
for (int32_t i = 0; i < g_state.processor_count; ++i)
614577
{
615-
for (sint32 i = 0; i < g_state.processor_count; ++i)
616-
{
617-
const uint64 kernel_diff = sppi[i].KernelTime.QuadPart - g_state.processor_times[i].kernel;
618-
const uint64 user_diff = sppi[i].UserTime.QuadPart - g_state.processor_times[i].user;
619-
const uint64 idle_diff = sppi[i].IdleTime.QuadPart - g_state.processor_times[i].idle;
620-
621-
const auto total = kernel_diff + user_diff; // kernel time already includes idletime
622-
const double cpu = total == 0 ? 0 : (1.0 - ((double)idle_diff / total)) * 100.0;
578+
double cpu = ProcessorTime::Compare(g_state.processor_times[i], now[i]);
623579

624-
g_state.cpu_per_core[i] = cpu;
625-
//total_cpu += cpu;
580+
g_state.cpu_per_core[i] = cpu * 100;
581+
g_state.processor_times[i] = now[i];
582+
}
583+
}
626584

627-
g_state.processor_times[i].idle = sppi[i].IdleTime.QuadPart;
628-
g_state.processor_times[i].kernel = sppi[i].KernelTime.QuadPart;
629-
g_state.processor_times[i].user = sppi[i].UserTime.QuadPart;
630-
}
585+
void LatteOverlay_updateStats(double fps, sint32 drawcalls)
586+
{
587+
if (GetConfig().overlay.position == ScreenPosition::kDisabled)
588+
return;
631589

632-
//total_cpu /= g_state.processor_count;
633-
//g_state.cpu_usage = total_cpu;
634-
}
590+
g_state.fps = fps;
591+
g_state.draw_calls_per_frame = drawcalls;
592+
UpdateStats_CemuCpu();
593+
UpdateStats_CpuPerCore();
635594

636595
// update ram
637-
PROCESS_MEMORY_COUNTERS pmc{};
638-
pmc.cb = sizeof(pmc);
639-
GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
640-
g_state.ram_usage = (pmc.WorkingSetSize / 1000) / 1000;
641-
#endif
596+
g_state.ram_usage = (QueryRamUsage() / 1000) / 1000;
642597

643598
// update vram
644599
g_renderer->GetVRAMInfo(g_state.vramUsage, g_state.vramTotal);

src/Cafe/HW/Latte/Core/LattePerformanceMonitor.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ void LattePerformanceMonitor_frameEnd()
9898
performanceMonitor.cycle[nextCycleIndex].recompilerLeaveCount = 0;
9999
performanceMonitor.cycle[nextCycleIndex].threadLeaveCount = 0;
100100
performanceMonitor.cycleIndex = nextCycleIndex;
101-
101+
102+
// next update in 1 second
103+
performanceMonitor.cycle[performanceMonitor.cycleIndex].lastUpdate = GetTickCount();
104+
102105
if (isFirstUpdate)
103106
{
104107
LatteOverlay_updateStats(0.0, 0);
@@ -109,8 +112,6 @@ void LattePerformanceMonitor_frameEnd()
109112
LatteOverlay_updateStats(fps, drawCallCounter / elapsedFrames);
110113
gui_updateWindowTitles(false, false, fps);
111114
}
112-
// next update in 1 second
113-
performanceMonitor.cycle[performanceMonitor.cycleIndex].lastUpdate = GetTickCount();
114115

115116
// prevent hibernation and screen saver/monitor off
116117
#if BOOST_OS_WINDOWS
@@ -124,4 +125,4 @@ void LattePerformanceMonitor_frameBegin()
124125
{
125126
performanceMonitor.vk.numDrawBarriersPerFrame.reset();
126127
performanceMonitor.vk.numBeginRenderpassPerFrame.reset();
127-
}
128+
}

src/util/CMakeLists.txt

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ add_library(CemuUtil
1616
DXGIWrapper/DXGIWrapper.h
1717
EventService.h
1818
Fiber/Fiber.h
19-
Fiber/FiberUnix.cpp
20-
Fiber/FiberWin.cpp
2119
helpers/ClassWrapper.h
2220
helpers/ConcurrentQueue.h
2321
helpers/enum_array.hpp
@@ -50,8 +48,8 @@ add_library(CemuUtil
5048
math/vector2.h
5149
math/vector3.h
5250
MemMapper/MemMapper.h
53-
MemMapper/MemMapperUnix.cpp
54-
MemMapper/MemMapperWin.cpp
51+
SystemInfo/SystemInfo.cpp
52+
SystemInfo/SystemInfo.h
5553
ThreadPool/ThreadPool.cpp
5654
ThreadPool/ThreadPool.h
5755
tinyxml2/tinyxml2.cpp
@@ -71,6 +69,23 @@ add_library(CemuUtil
7169
Zir/Passes/ZpIRRegisterAllocator.cpp
7270
)
7371

72+
if(WIN32)
73+
target_sources(CemuUtil PRIVATE Fiber/FiberWin.cpp)
74+
target_sources(CemuUtil PRIVATE MemMapper/MemMapperWin.cpp)
75+
target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoWin.cpp)
76+
elseif(UNIX)
77+
target_sources(CemuUtil PRIVATE Fiber/FiberUnix.cpp)
78+
target_sources(CemuUtil PRIVATE MemMapper/MemMapperUnix.cpp)
79+
target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoUnix.cpp)
80+
if(NOT APPLE)
81+
target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoLinux.cpp)
82+
else()
83+
target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoMac.cpp)
84+
endif()
85+
else()
86+
target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoStub.cpp)
87+
endif()
88+
7489
set_property(TARGET CemuUtil PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
7590

7691
target_include_directories(CemuUtil PUBLIC "../")

src/util/Fiber/FiberUnix.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include "Fiber.h"
2-
#if BOOST_OS_LINUX || BOOST_OS_MACOS
32
#include <ucontext.h>
43

54
thread_local Fiber* sCurrentFiber{};
@@ -52,5 +51,3 @@ void* Fiber::GetFiberPrivateData()
5251
{
5352
return sCurrentFiber->m_privateData;
5453
}
55-
56-
#endif

src/util/Fiber/FiberWin.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include "Fiber.h"
2-
#if BOOST_OS_WINDOWS
32
#include <Windows.h>
43

54
thread_local Fiber* sCurrentFiber{};
@@ -39,5 +38,3 @@ void* Fiber::GetFiberPrivateData()
3938
{
4039
return sCurrentFiber->m_privateData;
4140
}
42-
43-
#endif

src/util/MemMapper/MemMapperUnix.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "util/MemMapper/MemMapper.h"
22

3-
#if BOOST_OS_LINUX || BOOST_OS_MACOS
43
#include <unistd.h>
54
#include <sys/mman.h>
65

@@ -65,5 +64,3 @@ namespace MemMapper
6564
}
6665

6766
};
68-
69-
#endif

src/util/MemMapper/MemMapperWin.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
#include "util/MemMapper/MemMapper.h"
22

3-
#if BOOST_OS_WINDOWS
4-
53
#include <Windows.h>
64

75
namespace MemMapper
@@ -63,5 +61,3 @@ namespace MemMapper
6361
}
6462

6563
};
66-
67-
#endif

src/util/SystemInfo/SystemInfo.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include "util/SystemInfo/SystemInfo.h"
2+
3+
uint64 ProcessorTime::work()
4+
{
5+
return user + kernel;
6+
}
7+
8+
uint64 ProcessorTime::total()
9+
{
10+
return idle + user + kernel;
11+
}
12+
13+
double ProcessorTime::Compare(ProcessorTime &last, ProcessorTime &now)
14+
{
15+
auto dwork = now.work() - last.work();
16+
auto dtotal = now.total() - last.total();
17+
18+
return (double)dwork / dtotal;
19+
}
20+
21+
uint32 GetProcessorCount()
22+
{
23+
return std::thread::hardware_concurrency();
24+
}
25+
26+
void QueryProcTime(ProcessorTime &out)
27+
{
28+
uint64 now, user, kernel;
29+
QueryProcTime(now, user, kernel);
30+
31+
out.idle = now - (user + kernel);
32+
out.kernel = kernel;
33+
out.user = user;
34+
}

src/util/SystemInfo/SystemInfo.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#pragma once
2+
3+
struct ProcessorTime
4+
{
5+
uint64 idle{}, kernel{}, user{};
6+
7+
uint64 work();
8+
uint64 total();
9+
10+
static double Compare(ProcessorTime &last, ProcessorTime &now);
11+
};
12+
13+
uint32 GetProcessorCount();
14+
uint64 QueryRamUsage();
15+
void QueryProcTime(uint64 &out_now, uint64 &out_user, uint64 &out_kernel);
16+
void QueryProcTime(ProcessorTime &out);
17+
void QueryCoreTimes(uint32 count, std::vector<ProcessorTime>& out);
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#include "util/SystemInfo/SystemInfo.h"
2+
3+
#include <unistd.h>
4+
5+
uint64 QueryRamUsage()
6+
{
7+
static long page_size = sysconf(_SC_PAGESIZE);
8+
if (page_size == -1)
9+
{
10+
return 0;
11+
}
12+
13+
std::ifstream file("/proc/self/statm");
14+
if (file)
15+
{
16+
file.ignore(std::numeric_limits<std::streamsize>::max(), ' ');
17+
uint64 pages;
18+
file >> pages;
19+
20+
return pages * page_size;
21+
}
22+
return 0;
23+
}
24+
25+
void QueryCoreTimes(uint32 count, std::vector<ProcessorTime>& out)
26+
{
27+
std::ifstream file("/proc/stat");
28+
if (file)
29+
{
30+
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
31+
32+
for (auto i = 0; i < out.size(); ++i)
33+
{
34+
uint64 user, nice, kernel, idle;
35+
file.ignore(std::numeric_limits<std::streamsize>::max(), ' ');
36+
file >> user >> nice >> kernel >> idle;
37+
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
38+
39+
out[i].idle = idle;
40+
out[i].kernel = kernel;
41+
out[i].user = user + nice;
42+
}
43+
}
44+
else
45+
{
46+
for (auto i = 0; i < count; ++i) out[i] = { };
47+
}
48+
}

0 commit comments

Comments
 (0)