// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team // SPDX-License-Identifier: LGPL-3.0+ #if defined(__APPLE__) #include "common/Darwin/DarwinMisc.h" #include #include #include #include #include #include #include #include #include "common/Pcsx2Types.h" #include "common/Console.h" #include "common/HostSys.h" #include "common/Threading.h" #include "common/WindowInfo.h" // Darwin (OSX) is a bit different from Linux when requesting properties of // the OS because of its BSD/Mach heritage. Helpfully, most of this code // should translate pretty well to other *BSD systems. (e.g.: the sysctl(3) // interface). // // For an overview of all of Darwin's sysctls, check: // https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/sysctl.3.html // Return the total physical memory on the machine, in bytes. Returns 0 on // failure (not supported by the operating system). u64 GetPhysicalMemory() { u64 getmem = 0; size_t len = sizeof(getmem); int mib[] = {CTL_HW, HW_MEMSIZE}; if (sysctl(mib, std::size(mib), &getmem, &len, NULL, 0) < 0) perror("sysctl:"); return getmem; } static mach_timebase_info_data_t s_timebase_info; static const u64 tickfreq = []() { if (mach_timebase_info(&s_timebase_info) != KERN_SUCCESS) abort(); return (u64)1e9 * (u64)s_timebase_info.denom / (u64)s_timebase_info.numer; }(); // returns the performance-counter frequency: ticks per second (Hz) // // usage: // u64 seconds_passed = GetCPUTicks() / GetTickFrequency(); // u64 millis_passed = (GetCPUTicks() * 1000) / GetTickFrequency(); // // NOTE: multiply, subtract, ... your ticks before dividing by // GetTickFrequency() to maintain good precision. u64 GetTickFrequency() { return tickfreq; } // return the number of "ticks" since some arbitrary, fixed time in the // past. On OSX x86(-64), this is actually the number of nanoseconds passed, // because mach_timebase_info.numer == denom == 1. So "ticks" == // nanoseconds. u64 GetCPUTicks() { return mach_absolute_time(); } static std::string sysctl_str(int category, int name) { char buf[32]; size_t len = sizeof(buf); int mib[] = {category, name}; sysctl(mib, std::size(mib), buf, &len, nullptr, 0); return std::string(buf, len > 0 ? len - 1 : 0); } static std::optional sysctlbyname_u32(const char* name) { u32 output; size_t output_size = sizeof(output); if (0 != sysctlbyname(name, &output, &output_size, nullptr, 0)) return std::nullopt; if (output_size != sizeof(output)) { DevCon.WriteLn("(DarwinMisc) sysctl %s gave unexpected size %zd", name, output_size); return std::nullopt; } return output; } std::string GetOSVersionString() { std::string type = sysctl_str(CTL_KERN, KERN_OSTYPE); std::string release = sysctl_str(CTL_KERN, KERN_OSRELEASE); std::string arch = sysctl_str(CTL_HW, HW_MACHINE); return type + " " + release + " " + arch; } static IOPMAssertionID s_pm_assertion; bool WindowInfo::InhibitScreensaver(const WindowInfo& wi, bool inhibit) { if (s_pm_assertion) { IOPMAssertionRelease(s_pm_assertion); s_pm_assertion = 0; } if (inhibit) IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep, kIOPMAssertionLevelOn, CFSTR("Playing a game"), &s_pm_assertion); return true; } void Threading::Sleep(int ms) { usleep(1000 * ms); } void Threading::SleepUntil(u64 ticks) { // This is definitely sub-optimal, but apparently clock_nanosleep() doesn't exist. const s64 diff = static_cast(ticks - GetCPUTicks()); if (diff <= 0) return; const u64 nanos = (static_cast(diff) * static_cast(s_timebase_info.denom)) / static_cast(s_timebase_info.numer); if (nanos == 0) return; struct timespec ts; ts.tv_sec = nanos / 1000000000ULL; ts.tv_nsec = nanos % 1000000000ULL; nanosleep(&ts, nullptr); } std::vector DarwinMisc::GetCPUClasses() { std::vector out; if (std::optional nperflevels = sysctlbyname_u32("hw.nperflevels")) { char name[64]; for (u32 i = 0; i < *nperflevels; i++) { snprintf(name, sizeof(name), "hw.perflevel%u.physicalcpu", i); std::optional physicalcpu = sysctlbyname_u32(name); snprintf(name, sizeof(name), "hw.perflevel%u.logicalcpu", i); std::optional logicalcpu = sysctlbyname_u32(name); char levelname[64]; size_t levelname_size = sizeof(levelname); snprintf(name, sizeof(name), "hw.perflevel%u.name", i); if (0 != sysctlbyname(name, levelname, &levelname_size, nullptr, 0)) strcpy(levelname, "???"); if (!physicalcpu.has_value() || !logicalcpu.has_value()) { Console.Warning("(DarwinMisc) Perf level %u is missing data on %s cpus!", i, !physicalcpu.has_value() ? "physical" : "logical"); continue; } out.push_back({levelname, *physicalcpu, *logicalcpu}); } } else if (std::optional physcpu = sysctlbyname_u32("hw.physicalcpu")) { out.push_back({"Default", *physcpu, sysctlbyname_u32("hw.logicalcpu").value_or(0)}); } else { Console.Warning("(DarwinMisc) Couldn't get cpu core count!"); } return out; } #endif