Compare commits

...

5 Commits

Author SHA1 Message Date
Tilka 53e24b43fe
Merge 083e28b222 into e0c72cd963 2025-08-29 21:42:47 +02:00
JMC47 e0c72cd963
Merge pull request #13825 from jordan-woyak/dont-count-playtime-while-suspended
Common/Timer: Add a SteadyAwakeClock class to make play time tracking ignore time while suspended.
2025-08-28 13:58:26 -04:00
Tillmann Karras 083e28b222 Common: make use of AVX/AVX2 in CopySwapped() 2025-07-31 16:56:01 +01:00
Jordan Woyak 81ebf45c9b Core: Make play time tracking use SteadyAwakeClock. 2025-07-24 23:56:10 -05:00
Jordan Woyak 62c773ac75 Common/Timer: Add a SteadyAwakeClock class which counts non-suspended system running time. 2025-07-24 23:56:10 -05:00
5 changed files with 116 additions and 19 deletions

View File

@ -19,6 +19,7 @@
#include <fmt/format.h>
#include "Common/CommonTypes.h"
#include "Common/Intrinsics.h"
namespace Common
{
@ -168,6 +169,64 @@ inline T FromBigEndian(T data)
return data;
}
#ifdef __AVX__
// Byte-swap patterns for PSHUFB.
template <size_t ByteSize>
inline __m128i GetSwapShuffle128()
{
if constexpr (ByteSize == 2)
return _mm_set_epi64x(0x0e0f0c0d0a0b0809, 0x0607040502030001);
else if constexpr (ByteSize == 4)
return _mm_set_epi64x(0x0c0d0e0f08090a0b, 0x0405060700010203);
else if constexpr (ByteSize == 8)
return _mm_set_epi64x(0x08090a0b0c0d0e0f, 0x0001020304050607);
else
static_assert(false);
}
#endif
#ifdef __AVX2__
// Byte-swap patterns for VPSHUFB.
template <size_t ByteSize>
inline __m256i GetSwapShuffle256()
{
__m128i pattern = GetSwapShuffle128<ByteSize>();
return _mm256_set_m128i(pattern, pattern);
}
#endif
// Templated functions for byteswapped copies.
template <typename T>
inline void CopySwapped(T* dst, const T* src, size_t byte_size)
{
constexpr size_t S = sizeof(T);
const size_t count = byte_size / S;
size_t i = 0;
#ifdef __AVX2__
for (; i + 32 / S <= count; i += 32 / S)
{
const auto vdst = reinterpret_cast<__m256i*>(dst + i);
const auto vsrc = reinterpret_cast<const __m256i*>(src + i);
const auto swap = GetSwapShuffle256<S>();
_mm256_storeu_si256(vdst, _mm256_shuffle_epi8(_mm256_loadu_si256(vsrc), swap));
}
#endif
#ifdef __AVX__
for (; i + 16 / S <= count; i += 16 / S)
{
const auto vdst = reinterpret_cast<__m128i*>(dst + i);
const auto vsrc = reinterpret_cast<const __m128i*>(src + i);
const auto swap = GetSwapShuffle128<S>();
_mm_storeu_si128(vdst, _mm_shuffle_epi8(_mm_loadu_si128(vsrc), swap));
}
#endif
for (; i < count; ++i)
dst[i] = Common::FromBigEndian(src[i]);
}
template <typename value_type>
struct BigEndianValue
{

View File

@ -5,6 +5,7 @@
#include <chrono>
#include <thread>
#include "Common/CommonFuncs.h"
#ifdef _WIN32
#include <Windows.h>
@ -194,4 +195,37 @@ void PrecisionTimer::SleepUntil(Clock::time_point target)
}
}
// 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

View File

@ -50,4 +50,19 @@ private:
#endif
};
// Similar to std::chrono::steady_clock except this clock
// specifically does *not* count time while the system is suspended.
class SteadyAwakeClock
{
public:
using rep = s64;
using period = std::nano;
using duration = std::chrono::duration<rep, period>;
using time_point = std::chrono::time_point<SteadyAwakeClock>;
static constexpr bool is_steady = true;
static time_point now();
};
} // Namespace Common

View File

@ -72,8 +72,8 @@ void CPUManager::StartTimePlayedTimer()
{
Common::SetCurrentThreadName("Play Time Tracker");
// Steady clock for greater accuracy of timing
std::chrono::steady_clock timer;
// Use a clock that will appropriately ignore suspended system time.
Common::SteadyAwakeClock timer;
auto prev_time = timer.now();
while (true)

View File

@ -130,29 +130,18 @@ public:
void Write_U32_Swap(u32 var, u32 address);
void Write_U64_Swap(u64 var, u32 address);
// Templated functions for byteswapped copies.
template <typename T>
void CopyFromEmuSwapped(T* data, u32 address, size_t size) const
void CopyFromEmuSwapped(T* dst, u32 address, size_t size)
{
const T* src = reinterpret_cast<T*>(GetPointerForRange(address, size));
if (src == nullptr)
return;
for (size_t i = 0; i < size / sizeof(T); i++)
data[i] = Common::FromBigEndian(src[i]);
if (const T* src = reinterpret_cast<T*>(GetPointerForRange(address, size)))
Common::CopySwapped(dst, src, size);
}
template <typename T>
void CopyToEmuSwapped(u32 address, const T* data, size_t size)
void CopyToEmuSwapped(u32 address, const T* src, size_t size)
{
T* dest = reinterpret_cast<T*>(GetPointerForRange(address, size));
if (dest == nullptr)
return;
for (size_t i = 0; i < size / sizeof(T); i++)
dest[i] = Common::FromBigEndian(data[i]);
if (T* dst = reinterpret_cast<T*>(GetPointerForRange(address, size)))
Common::CopySwapped(dst, src, size);
}
private: