mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-14 16:33:52 +00:00
- memory_monitor: extract kOneGB and kFallbackRAM constants from 6 duplicated 1024*1024*1024 expressions; name kFieldPrefixLen for /proc/meminfo "MemAvailable:" offset (was bare 13) - camera: add why-comment on projection matrix jitter — column 2 holds NDC x/y offset for TAA/FSR2 sub-pixel sampling - movement_handler: name kMaxTaxiNodeId (384) with why-comment — WotLK TaxiNodes.dbc has 384 entries, bitmask is 12 × uint32
130 lines
4.1 KiB
C++
130 lines
4.1 KiB
C++
#include "core/memory_monitor.hpp"
|
|
#include "core/logger.hpp"
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <sstream>
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#elif defined(__APPLE__)
|
|
#include <sys/types.h>
|
|
#include <sys/sysctl.h>
|
|
#else
|
|
#include <sys/sysinfo.h>
|
|
#endif
|
|
|
|
namespace wowee {
|
|
namespace core {
|
|
|
|
#if !defined(_WIN32) && !defined(__APPLE__)
|
|
namespace {
|
|
size_t readMemAvailableBytesFromProc() {
|
|
std::ifstream meminfo("/proc/meminfo");
|
|
if (!meminfo.is_open()) return 0;
|
|
|
|
std::string line;
|
|
while (std::getline(meminfo, line)) {
|
|
// /proc/meminfo format: "MemAvailable: 123456789 kB"
|
|
static constexpr size_t kFieldPrefixLen = 13; // strlen("MemAvailable:")
|
|
if (line.rfind("MemAvailable:", 0) != 0) continue;
|
|
std::istringstream iss(line.substr(kFieldPrefixLen));
|
|
size_t kb = 0;
|
|
iss >> kb;
|
|
if (kb > 0) return kb * 1024ull;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
} // namespace
|
|
#endif // !_WIN32 && !__APPLE__
|
|
|
|
MemoryMonitor& MemoryMonitor::getInstance() {
|
|
static MemoryMonitor instance;
|
|
return instance;
|
|
}
|
|
|
|
void MemoryMonitor::initialize() {
|
|
constexpr size_t kOneGB = 1024ull * 1024 * 1024;
|
|
// Fallback if OS API unavailable — 16 GB is a safe conservative estimate
|
|
// that prevents over-aggressive asset caching on unknown hardware.
|
|
constexpr size_t kFallbackRAM = 16 * kOneGB;
|
|
|
|
#ifdef _WIN32
|
|
ULONGLONG totalKB = 0;
|
|
if (GetPhysicallyInstalledSystemMemory(&totalKB)) {
|
|
totalRAM_ = static_cast<size_t>(totalKB) * 1024ull;
|
|
LOG_INFO("System RAM detected: ", totalRAM_ / kOneGB, " GB");
|
|
} else {
|
|
totalRAM_ = kFallbackRAM;
|
|
LOG_WARNING("Could not detect system RAM, assuming 16GB");
|
|
}
|
|
#elif defined(__APPLE__)
|
|
int64_t physmem = 0;
|
|
size_t len = sizeof(physmem);
|
|
if (sysctlbyname("hw.memsize", &physmem, &len, nullptr, 0) == 0) {
|
|
totalRAM_ = static_cast<size_t>(physmem);
|
|
LOG_INFO("System RAM detected: ", totalRAM_ / kOneGB, " GB");
|
|
} else {
|
|
totalRAM_ = kFallbackRAM;
|
|
LOG_WARNING("Could not detect system RAM, assuming 16GB");
|
|
}
|
|
#else
|
|
struct sysinfo info;
|
|
if (sysinfo(&info) == 0) {
|
|
totalRAM_ = static_cast<size_t>(info.totalram) * info.mem_unit;
|
|
LOG_INFO("System RAM detected: ", totalRAM_ / kOneGB, " GB");
|
|
} else {
|
|
totalRAM_ = kFallbackRAM;
|
|
LOG_WARNING("Could not detect system RAM, assuming 16GB");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
size_t MemoryMonitor::getAvailableRAM() const {
|
|
#ifdef _WIN32
|
|
MEMORYSTATUSEX status;
|
|
status.dwLength = sizeof(status);
|
|
if (GlobalMemoryStatusEx(&status)) {
|
|
return static_cast<size_t>(status.ullAvailPhys);
|
|
}
|
|
return totalRAM_ / 2;
|
|
#elif defined(__APPLE__)
|
|
int64_t usermem = 0;
|
|
size_t len = sizeof(usermem);
|
|
if (sysctlbyname("hw.usermem", &usermem, &len, nullptr, 0) == 0) {
|
|
return static_cast<size_t>(usermem);
|
|
}
|
|
return totalRAM_ / 2;
|
|
#else
|
|
// Best source on Linux for reclaimable memory headroom.
|
|
if (size_t memAvailable = readMemAvailableBytesFromProc(); memAvailable > 0) {
|
|
return memAvailable;
|
|
}
|
|
|
|
struct sysinfo info;
|
|
if (sysinfo(&info) == 0) {
|
|
// Fallback approximation if /proc/meminfo is unavailable.
|
|
size_t freeBytes = static_cast<size_t>(info.freeram) * info.mem_unit;
|
|
size_t bufferBytes = static_cast<size_t>(info.bufferram) * info.mem_unit;
|
|
size_t available = freeBytes + bufferBytes;
|
|
return (totalRAM_ > 0 && available > totalRAM_) ? totalRAM_ : available;
|
|
}
|
|
return totalRAM_ / 2; // Fallback: assume 50% available
|
|
#endif
|
|
}
|
|
|
|
size_t MemoryMonitor::getRecommendedCacheBudget() const {
|
|
size_t available = getAvailableRAM();
|
|
// Use 50% of available RAM for caches, hard-capped at 16 GB.
|
|
static constexpr size_t kHardCapBytes = 16ull * 1024 * 1024 * 1024; // 16 GB
|
|
size_t budget = available * 50 / 100;
|
|
return budget < kHardCapBytes ? budget : kHardCapBytes;
|
|
}
|
|
|
|
bool MemoryMonitor::isMemoryPressure() const {
|
|
size_t available = getAvailableRAM();
|
|
// Memory pressure if < 10% RAM available
|
|
return available < (totalRAM_ * 10 / 100);
|
|
}
|
|
|
|
} // namespace core
|
|
} // namespace wowee
|