dolphin/Source/Core/Common/Timer.cpp
Martino Fontana a14c88ba67 Remove unused imports
Yellow squiggly lines begone!
Done automatically on .cpp files through `run-clang-tidy`, with manual corrections to the mistakes.
If an import is directly used, but is technically unnecessary since it's recursively imported by something else, it is *not* removed.
The tool doesn't touch .h files, so I did some of them by hand while fixing errors due to old recursive imports.
Not everything is removed, but the cleanup should be substantial enough.
Because this done on Linux, code that isn't used on it is mostly untouched.
(Hopefully no open PR is depending on these imports...)
2026-01-25 16:12:15 +01:00

230 lines
6.4 KiB
C++

// Copyright 2008 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Common/Timer.h"
#include <chrono>
#include <thread>
#include "Common/CommonFuncs.h"
#ifdef _WIN32
#include <Windows.h>
#include <timeapi.h>
#endif
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
namespace Common
{
template <typename Clock, typename Duration>
static typename Clock::rep time_now()
{
return std::chrono::time_point_cast<Duration>(Clock::now()).time_since_epoch().count();
}
template <typename Duration>
static auto steady_time_now()
{
return time_now<std::chrono::steady_clock, Duration>();
}
u64 Timer::NowUs()
{
return steady_time_now<std::chrono::microseconds>();
}
u64 Timer::NowMs()
{
return steady_time_now<std::chrono::milliseconds>();
}
void Timer::Start()
{
m_start_ms = NowMs();
m_end_ms = 0;
m_running = true;
}
void Timer::StartWithOffset(u64 offset)
{
Start();
m_start_ms -= offset;
}
void Timer::Stop()
{
m_end_ms = NowMs();
m_running = false;
}
u64 Timer::ElapsedMs() const
{
const u64 end = m_running ? NowMs() : m_end_ms;
// Can handle up to 1 rollover event (underflow produces correct result)
// If Start() has never been called, will return 0
return end - m_start_ms;
}
u64 Timer::GetLocalTimeSinceJan1970()
{
#ifdef _MSC_VER
std::chrono::zoned_seconds seconds(
std::chrono::current_zone(),
std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()));
return seconds.get_local_time().time_since_epoch().count();
#else
time_t sysTime, tzDiff, tzDST;
time(&sysTime);
tm* gmTime = localtime(&sysTime);
// Account for DST where needed
if (gmTime->tm_isdst == 1)
tzDST = 3600;
else
tzDST = 0;
// Lazy way to get local time in sec
gmTime = gmtime(&sysTime);
tzDiff = sysTime - mktime(gmTime);
return static_cast<u64>(sysTime + tzDiff + tzDST);
#endif
}
// This is requested by Timer::IncreaseResolution on Windows.
// On Linux/other we hope/assume we already have 1ms scheduling granularity.
static constexpr int TIMER_RESOLUTION_MS = 1;
void Timer::IncreaseResolution()
{
#ifdef _WIN32
// Disable execution speed and timer resolution throttling process-wide.
// This mainly will keep Dolphin marked as high performance if it's in the background. The OS
// should make it high performance if it's in the foreground anyway (or for some specific
// threads e.g. audio).
// This is best-effort (i.e. the call may fail on older versions of Windows, where such throttling
// doesn't exist, anyway), and we don't bother reverting once set.
// This adjusts behavior on CPUs with "performance" and "efficiency" cores
PROCESS_POWER_THROTTLING_STATE PowerThrottling{};
PowerThrottling.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION;
PowerThrottling.ControlMask =
PROCESS_POWER_THROTTLING_EXECUTION_SPEED | PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION;
PowerThrottling.StateMask = 0;
SetProcessInformation(GetCurrentProcess(), ProcessPowerThrottling, &PowerThrottling,
sizeof(PowerThrottling));
// Not actually sure how useful this is these days.. :')
timeBeginPeriod(TIMER_RESOLUTION_MS);
#endif
}
void Timer::RestoreResolution()
{
#ifdef _WIN32
timeEndPeriod(TIMER_RESOLUTION_MS);
#endif
}
PrecisionTimer::PrecisionTimer()
{
#if defined(_WIN32)
// "TIMER_HIGH_RESOLUTION" requires Windows 10, version 1803, and later.
m_timer_handle =
CreateWaitableTimerExW(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
if (m_timer_handle == NULL)
{
ERROR_LOG_FMT(COMMON, "CREATE_WAITABLE_TIMER_HIGH_RESOLUTION: Error:{}", GetLastError());
// Create a normal timer if "HIGH_RESOLUTION" isn't available.
m_timer_handle = CreateWaitableTimerExW(NULL, NULL, 0, TIMER_ALL_ACCESS);
if (m_timer_handle == NULL)
ERROR_LOG_FMT(COMMON, "CreateWaitableTimerExW: Error:{}", GetLastError());
}
#endif
}
PrecisionTimer::~PrecisionTimer()
{
#if defined(_WIN32)
CloseHandle(m_timer_handle);
#endif
}
void PrecisionTimer::SleepUntil(Clock::time_point target)
{
constexpr auto SPIN_TIME =
std::chrono::milliseconds{TIMER_RESOLUTION_MS} + std::chrono::microseconds{20};
#if defined(_WIN32)
while (true)
{
// SetWaitableTimerEx takes time in "100 nanosecond intervals".
using TimerDuration = std::chrono::duration<LONGLONG, std::ratio<100, std::nano::den>::type>;
// Apparently waiting longer than the timer resolution gives terrible accuracy.
// We'll wait in steps of 95% of the time period.
constexpr auto MAX_TICKS =
duration_cast<TimerDuration>(std::chrono::milliseconds{TIMER_RESOLUTION_MS}) * 95 / 100;
const auto wait_time = target - Clock::now() - SPIN_TIME;
const auto ticks = std::min(duration_cast<TimerDuration>(wait_time), MAX_TICKS).count();
if (ticks <= 0)
break;
const LARGE_INTEGER due_time{.QuadPart = -ticks};
SetWaitableTimerEx(m_timer_handle, &due_time, 0, NULL, NULL, NULL, 0);
WaitForSingleObject(m_timer_handle, INFINITE);
}
#else
// Sleeping on Linux generally isn't as terrible as it is on Windows.
std::this_thread::sleep_until(target - SPIN_TIME);
#endif
// Spin for the remaining time.
while (Clock::now() < target)
{
#if defined(_WIN32)
YieldProcessor();
#else
std::this_thread::yield();
#endif
}
}
// Results are appropriately slewed on Linux, but not on Windows, macOS, or FreeBSD.
// Clocks with that functionality seem to not be available there.
auto SteadyAwakeClock::now() -> time_point
{
#if defined(_WIN32)
// The count is system time "in units of 100 nanoseconds".
using InterruptDuration = std::chrono::duration<ULONGLONG, std::ratio<100, std::nano::den>::type>;
ULONGLONG interrupt_time{};
if (!QueryUnbiasedInterruptTime(&interrupt_time))
ERROR_LOG_FMT(COMMON, "QueryUnbiasedInterruptTime");
return time_point{InterruptDuration{interrupt_time}};
#else
// Note that Linux's CLOCK_MONOTONIC "does not count time that the system is suspended".
// This is in contrast to the behavior on macOS and FreeBSD.
static constexpr auto clock_id =
#if defined(__linux__)
CLOCK_MONOTONIC;
#elif defined(__APPLE__)
CLOCK_UPTIME_RAW;
#else
CLOCK_UPTIME;
#endif
timespec ts{};
if (clock_gettime(clock_id, &ts) != 0)
ERROR_LOG_FMT(COMMON, "clock_gettime: {}", LastStrerrorString());
return time_point{std::chrono::seconds{ts.tv_sec} + std::chrono::nanoseconds{ts.tv_nsec}};
#endif
}
} // Namespace Common