/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see .
*/
#include "PrecompiledHeader.h"
#include "Timer.h"
#include
#include
#if defined(_WIN32)
#include "RedtapeWindows.h"
#elif defined(__APPLE__)
#include
#include
#include
#else
#include
#include
#include
#include
#endif
namespace Common
{
#ifdef _WIN32
static double s_counter_frequency;
static bool s_counter_initialized = false;
Timer::Value Timer::GetCurrentValue()
{
// even if this races, it should still result in the same value..
if (!s_counter_initialized)
{
LARGE_INTEGER Freq;
QueryPerformanceFrequency(&Freq);
s_counter_frequency = static_cast(Freq.QuadPart) / 1000000000.0;
s_counter_initialized = true;
}
Timer::Value ReturnValue;
QueryPerformanceCounter(reinterpret_cast(&ReturnValue));
return ReturnValue;
}
double Timer::ConvertValueToNanoseconds(Timer::Value value)
{
return (static_cast(value) / s_counter_frequency);
}
double Timer::ConvertValueToMilliseconds(Timer::Value value)
{
return ((static_cast(value) / s_counter_frequency) / 1000000.0);
}
double Timer::ConvertValueToSeconds(Timer::Value value)
{
return ((static_cast(value) / s_counter_frequency) / 1000000000.0);
}
Timer::Value Timer::ConvertSecondsToValue(double s)
{
return static_cast((s * 1000000000.0) * s_counter_frequency);
}
Timer::Value Timer::ConvertMillisecondsToValue(double ms)
{
return static_cast((ms * 1000000.0) * s_counter_frequency);
}
Timer::Value Timer::ConvertNanosecondsToValue(double ns)
{
return static_cast(ns * s_counter_frequency);
}
#else
Timer::Value Timer::GetCurrentValue()
{
struct timespec tv;
clock_gettime(CLOCK_MONOTONIC, &tv);
return ((Value)tv.tv_nsec + (Value)tv.tv_sec * 1000000000);
}
double Timer::ConvertValueToNanoseconds(Timer::Value value)
{
return static_cast(value);
}
double Timer::ConvertValueToMilliseconds(Timer::Value value)
{
return (static_cast(value) / 1000000.0);
}
double Timer::ConvertValueToSeconds(Timer::Value value)
{
return (static_cast(value) / 1000000000.0);
}
Timer::Value Timer::ConvertSecondsToValue(double s)
{
return static_cast(s * 1000000000.0);
}
Timer::Value Timer::ConvertMillisecondsToValue(double ms)
{
return static_cast(ms * 1000000.0);
}
Timer::Value Timer::ConvertNanosecondsToValue(double ns)
{
return static_cast(ns);
}
#endif
Timer::Timer()
{
Reset();
}
void Timer::Reset()
{
m_tvStartValue = GetCurrentValue();
}
double Timer::GetTimeSeconds() const
{
return ConvertValueToSeconds(GetCurrentValue() - m_tvStartValue);
}
double Timer::GetTimeMilliseconds() const
{
return ConvertValueToMilliseconds(GetCurrentValue() - m_tvStartValue);
}
double Timer::GetTimeNanoseconds() const
{
return ConvertValueToNanoseconds(GetCurrentValue() - m_tvStartValue);
}
double Timer::GetTimeSecondsAndReset()
{
const Value value = GetCurrentValue();
const double ret = ConvertValueToSeconds(value - m_tvStartValue);
m_tvStartValue = value;
return ret;
}
double Timer::GetTimeMillisecondsAndReset()
{
const Value value = GetCurrentValue();
const double ret = ConvertValueToMilliseconds(value - m_tvStartValue);
m_tvStartValue = value;
return ret;
}
double Timer::GetTimeNanosecondsAndReset()
{
const Value value = GetCurrentValue();
const double ret = ConvertValueToNanoseconds(value - m_tvStartValue);
m_tvStartValue = value;
return ret;
}
ThreadCPUTimer::ThreadCPUTimer() = default;
ThreadCPUTimer::ThreadCPUTimer(ThreadCPUTimer&& move)
: m_thread_handle(move.m_thread_handle)
{
move.m_thread_handle = nullptr;
}
ThreadCPUTimer::~ThreadCPUTimer()
{
#ifdef _WIN32
CloseHandle(reinterpret_cast(m_thread_handle));
#endif
}
ThreadCPUTimer& ThreadCPUTimer::operator=(ThreadCPUTimer&& move)
{
std::swap(m_thread_handle, move.m_thread_handle);
return *this;
}
void ThreadCPUTimer::Reset()
{
m_start_value = GetCurrentValue();
}
ThreadCPUTimer::Value ThreadCPUTimer::GetCurrentValue() const
{
#if defined(_WIN32)
FILETIME create, exit, user, kernel;
if (!m_thread_handle || !GetThreadTimes((HANDLE)m_thread_handle, &create, &exit, &user, &kernel))
return 0;
Value value = (static_cast(user.dwHighDateTime) << 32) | (static_cast(user.dwLowDateTime));
value += (static_cast(kernel.dwHighDateTime) << 32) | (static_cast(kernel.dwLowDateTime));
return value;
#elif defined(__APPLE__)
thread_basic_info_data_t info;
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
const kern_return_t kr = thread_info((mach_port_t) reinterpret_cast(m_thread_handle), THREAD_BASIC_INFO, (thread_info_t)&info, &count);
if (kr != KERN_SUCCESS)
return 0;
Value value = (static_cast(info.user_time.seconds) * 1000000) + (static_cast(info.user_time.microseconds));
value += (static_cast(info.system_time.seconds) * 1000000) + (static_cast(info.system_time.microseconds));
return value;
#else
clockid_t cid;
if (!m_thread_handle || pthread_getcpuclockid((pthread_t)m_thread_handle, &cid) != 0)
return 0;
struct timespec ts;
if (clock_gettime(cid, &ts) != 0)
return 0;
return (static_cast(ts.tv_nsec) + static_cast(ts.tv_sec) * 1000000000LL);
#endif
}
double ThreadCPUTimer::GetTimeSeconds() const
{
return ConvertValueToSeconds(GetCurrentValue() - m_start_value);
}
double ThreadCPUTimer::GetTimeMilliseconds() const
{
return ConvertValueToMilliseconds(GetCurrentValue() - m_start_value);
}
double ThreadCPUTimer::GetTimeNanoseconds() const
{
return ConvertValueToNanoseconds(GetCurrentValue() - m_start_value);
}
void ThreadCPUTimer::GetUsageInSecondsAndReset(Value time_diff, double* usage_time, double* usage_percent)
{
const Value new_value = GetCurrentValue();
const Value diff = new_value - m_start_value;
m_start_value = new_value;
*usage_time = ConvertValueToSeconds(diff);
*usage_percent = GetUtilizationPercentage(time_diff, diff);
}
void ThreadCPUTimer::GetUsageInMillisecondsAndReset(Value time_diff, double* usage_time, double* usage_percent)
{
const Value new_value = GetCurrentValue();
const Value diff = new_value - m_start_value;
m_start_value = new_value;
*usage_time = ConvertValueToMilliseconds(diff);
*usage_percent = GetUtilizationPercentage(time_diff, diff);
}
void ThreadCPUTimer::GetUsageInNanosecondsAndReset(Value time_diff, double* usage_time, double* usage_percent)
{
const Value new_value = GetCurrentValue();
const Value diff = new_value - m_start_value;
m_start_value = new_value;
*usage_time = ConvertValueToNanoseconds(diff);
*usage_percent = GetUtilizationPercentage(time_diff, diff);
}
double ThreadCPUTimer::GetUtilizationPercentage(Timer::Value time_diff, Value cpu_time_diff)
{
#if defined(_WIN32)
return ((static_cast(cpu_time_diff) * 10000.0) / (static_cast(time_diff) / s_counter_frequency));
#elif defined(__APPLE__)
// microseconds, but time_tiff is in nanoseconds, so multiply by 1000 * 100
return (static_cast(cpu_time_diff) * 100000.0) / static_cast(time_diff);
#else
// nanoseconds
return (static_cast(cpu_time_diff) * 100.0) / static_cast(time_diff);
#endif
}
double ThreadCPUTimer::ConvertValueToSeconds(Value value)
{
#if defined(_WIN32)
// 100ns units
return (static_cast(value) / 10000000.0);
#elif defined(__APPLE__)
// microseconds
return (static_cast(value) / 1000000.0);
#else
// nanoseconds
return (static_cast(value) / 1000000000.0);
#endif
}
double ThreadCPUTimer::ConvertValueToMilliseconds(Value value)
{
#if defined(_WIN32)
return (static_cast(value) / 10000.0);
#elif defined(__APPLE__)
return (static_cast(value) / 1000.0);
#else
return (static_cast(value) / 1000000.0);
#endif
}
double ThreadCPUTimer::ConvertValueToNanoseconds(Value value)
{
#if defined(_WIN32)
return (static_cast(value) * 100.0);
#elif defined(__APPLE__)
return (static_cast(value) * 1000.0);
#else
return static_cast(value);
#endif
}
ThreadCPUTimer ThreadCPUTimer::GetForCallingThread()
{
ThreadCPUTimer ret;
#if defined(_WIN32)
ret.m_thread_handle = (void*)OpenThread(THREAD_QUERY_INFORMATION, FALSE, GetCurrentThreadId());
#elif defined(__APPLE__)
ret.m_thread_handle = reinterpret_cast((uintptr_t)mach_thread_self());
#else
ret.m_thread_handle = (void*)pthread_self();
#endif
ret.Reset();
return ret;
}
} // namespace Common