diff --git a/rts/System/BitUtils.h b/rts/System/BitUtils.h new file mode 100644 index 00000000000..4bb70b822c5 --- /dev/null +++ b/rts/System/BitUtils.h @@ -0,0 +1,21 @@ +/* This file is part of the Recoil engine (GPL v2 or later), see LICENSE.html */ + +#pragma once + +#include +#include + +namespace spring { + +// Returns a mask with the low `n` bits set. Saturates at the width of T to +// avoid the undefined behavior of shifting by >= the type's bit width. +template +constexpr T LowBitsMask(unsigned int n) { + if (n == 0) + return T(0); + if (n >= sizeof(T) * CHAR_BIT) + return ~T(0); + return (T(1) << n) - T(1); +} + +} // namespace spring diff --git a/rts/System/Platform/Mac/CpuTopology.cpp b/rts/System/Platform/Mac/CpuTopology.cpp new file mode 100644 index 00000000000..0f1f6be0a1d --- /dev/null +++ b/rts/System/Platform/Mac/CpuTopology.cpp @@ -0,0 +1,88 @@ +/* This file is part of the Recoil engine (GPL v2 or later), see LICENSE.html */ + +#include "System/Platform/CpuTopology.h" +#include "System/BitUtils.h" +#include +#include + +namespace cpu_topology { + +namespace { + +// Read an unsigned sysctl value by name. Returns 0 if the key is unavailable +// (e.g. perflevel keys on Intel Macs or pre-Apple-Silicon kernels). +unsigned int ReadSysctlUInt(const char* name) { + int value = 0; + size_t valueSize = sizeof(value); + if (sysctlbyname(name, &value, &valueSize, nullptr, 0) != 0) + return 0; + return (value > 0) ? static_cast(value) : 0; +} + +} // namespace + +ThreadPinPolicy GetThreadPinPolicy() { + // macOS has no pthread_setaffinity_np equivalent. Scheduling locality is + // instead expressed via QOS classes; see Platform/Mac/ThreadSupport.cpp. + return THREAD_PIN_POLICY_NONE; +} + +ProcessorMasks GetProcessorMasks() { + ProcessorMasks masks{}; + + // Apple Silicon exposes per-perflevel core counts. perflevel0 is the + // high-performance (P) cluster; perflevel1, when present, is the + // efficiency (E) cluster. Intel Macs and older kernels do not expose + // these keys; fall back to treating every core as a P-core there. + const unsigned int numPCores = ReadSysctlUInt("hw.perflevel0.physicalcpu"); + const unsigned int numECores = ReadSysctlUInt("hw.perflevel1.physicalcpu"); + + if (numPCores > 0) { + masks.performanceCoreMask = spring::LowBitsMask(numPCores); + // E-cores occupy the bits above the P-cores in the combined mask. + const unsigned int totalCores = numPCores + numECores; + const unsigned int allMask = spring::LowBitsMask(totalCores); + masks.efficiencyCoreMask = allMask & ~masks.performanceCoreMask; + } else { + // Intel Mac / unknown topology: treat the visible core count as + // homogeneous P-cores. Matches prior behavior on those targets. + unsigned int numCores = std::thread::hardware_concurrency(); + if (numCores == 0) numCores = 4; + masks.performanceCoreMask = spring::LowBitsMask(numCores); + masks.efficiencyCoreMask = 0; + } + + // macOS does not expose SMT/HT details; report all visible cores as + // hyper-thread-low so callers consuming those masks stay consistent. + masks.hyperThreadLowMask = masks.performanceCoreMask | masks.efficiencyCoreMask; + masks.hyperThreadHighMask = 0; + + return masks; +} + +ProcessorCaches GetProcessorCache() { + ProcessorCaches caches; + + ProcessorGroupCaches group; + unsigned int numCores = std::thread::hardware_concurrency(); + if (numCores == 0) numCores = 4; + group.groupMask = spring::LowBitsMask(numCores); + + // Try to get cache sizes via sysctl + size_t size = sizeof(uint64_t); + uint64_t cacheSize = 0; + + if (sysctlbyname("hw.l1dcachesize", &cacheSize, &size, nullptr, 0) == 0) + group.cacheSizes[0] = static_cast(cacheSize); + + if (sysctlbyname("hw.l2cachesize", &cacheSize, &size, nullptr, 0) == 0) + group.cacheSizes[1] = static_cast(cacheSize); + + if (sysctlbyname("hw.l3cachesize", &cacheSize, &size, nullptr, 0) == 0) + group.cacheSizes[2] = static_cast(cacheSize); + + caches.groupCaches.push_back(group); + return caches; +} + +} // namespace cpu_topology diff --git a/tools/unitsync/CMakeLists.txt b/tools/unitsync/CMakeLists.txt index 00f124f0a78..f7f5169e539 100644 --- a/tools/unitsync/CMakeLists.txt +++ b/tools/unitsync/CMakeLists.txt @@ -107,11 +107,16 @@ if (WIN32) list(APPEND main_files "${ENGINE_SRC_ROOT}/System/Platform/Win/WinVersion.cpp") list(APPEND main_files "${ENGINE_SRC_ROOT}/System/Platform/SharedLib.cpp") list(APPEND main_files "${ENGINE_SRC_ROOT}/System/Platform/Win/DllLib.cpp") -else (WIN32) +elseif (APPLE) + list(APPEND main_files "${ENGINE_SRC_ROOT}/System/Platform/Mac/CpuTopology.cpp") + list(APPEND main_files "${ENGINE_SRC_ROOT}/System/Platform/Linux/Hardware.cpp") + list(APPEND main_files "${ENGINE_SRC_ROOT}/System/Platform/SharedLib.cpp") + list(APPEND main_files "${ENGINE_SRC_ROOT}/System/Platform/Linux/SoLib.cpp") +else () list(APPEND main_files "${ENGINE_SRC_ROOT}/System/Platform/Linux/CpuTopology.cpp") list(APPEND main_files "${ENGINE_SRC_ROOT}/System/Platform/Linux/Hardware.cpp") list(APPEND main_files "${ENGINE_SRC_ROOT}/System/Platform/Linux/ThreadSupport.cpp") -endif (WIN32) +endif () set(unitsync_files ${sources_engine_System_FileSystem}