mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-10-12 02:08:45 +00:00
All games use mailbox/triple buffering. Except when you enable sync to host refresh, in which case FIFO/double buffering is used. This means vsync enabled will ever tear, but at the same time, never drop to 30fps on a missed frame due to frame rate differences. To have the "best of both worlds", you should enable vsync and sync to host refresh. Previously, this resulted in additional input lag, since the host vsync would drive the EE frame timing. Now, this behaviour is disabled by default, unless you enable "Use Host VSync Timing".
185 lines
5.1 KiB
C++
185 lines
5.1 KiB
C++
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
|
// SPDX-License-Identifier: LGPL-3.0+
|
|
|
|
#if defined(__APPLE__)
|
|
|
|
#include "common/Darwin/DarwinMisc.h"
|
|
#include "common/HostSys.h"
|
|
|
|
#include <cstring>
|
|
#include <cstdlib>
|
|
#include <optional>
|
|
#include <sys/types.h>
|
|
#include <sys/sysctl.h>
|
|
#include <time.h>
|
|
#include <mach/mach_time.h>
|
|
#include <IOKit/pwr_mgt/IOPMLib.h>
|
|
|
|
#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<u32> 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 Common::InhibitScreensaver(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<s64>(ticks - GetCPUTicks());
|
|
if (diff <= 0)
|
|
return;
|
|
|
|
const u64 nanos = (static_cast<u64>(diff) * static_cast<u64>(s_timebase_info.denom)) / static_cast<u64>(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::CPUClass> DarwinMisc::GetCPUClasses()
|
|
{
|
|
std::vector<CPUClass> out;
|
|
|
|
if (std::optional<u32> 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<u32> physicalcpu = sysctlbyname_u32(name);
|
|
snprintf(name, sizeof(name), "hw.perflevel%u.logicalcpu", i);
|
|
std::optional<u32> 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<u32> 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
|