mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-10-06 18:33:34 +00:00
TimeUtil: Minor cleanup, add precise_sleep()
This commit is contained in:
parent
5926886c0c
commit
96c4ae4457
@ -41,20 +41,27 @@ static LARGE_INTEGER frequency;
|
||||
static double frequencyMult;
|
||||
static LARGE_INTEGER startTime;
|
||||
|
||||
static inline void InitTime() {
|
||||
if (frequency.QuadPart == 0) {
|
||||
QueryPerformanceFrequency(&frequency);
|
||||
QueryPerformanceCounter(&startTime);
|
||||
frequencyMult = 1.0 / static_cast<double>(frequency.QuadPart);
|
||||
}
|
||||
HANDLE Timer;
|
||||
int SchedulerPeriodMs;
|
||||
INT64 QpcPerSecond;
|
||||
|
||||
void TimeInit() {
|
||||
QueryPerformanceFrequency(&frequency);
|
||||
QueryPerformanceCounter(&startTime);
|
||||
QpcPerSecond = frequency.QuadPart;
|
||||
frequencyMult = 1.0 / static_cast<double>(frequency.QuadPart);
|
||||
|
||||
Timer = CreateWaitableTimerExW(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
|
||||
TIMECAPS caps;
|
||||
timeGetDevCaps(&caps, sizeof caps);
|
||||
timeBeginPeriod(caps.wPeriodMin);
|
||||
SchedulerPeriodMs = (int)caps.wPeriodMin;
|
||||
}
|
||||
|
||||
double time_now_d() {
|
||||
InitTime();
|
||||
LARGE_INTEGER time;
|
||||
QueryPerformanceCounter(&time);
|
||||
double elapsed = static_cast<double>(time.QuadPart - startTime.QuadPart);
|
||||
return elapsed * frequencyMult;
|
||||
return static_cast<double>(time.QuadPart - startTime.QuadPart) * frequencyMult;
|
||||
}
|
||||
|
||||
// Fake, but usable in a pinch. Don't, though.
|
||||
@ -91,24 +98,28 @@ void yield() {
|
||||
YieldProcessor();
|
||||
}
|
||||
|
||||
TimeSpan::TimeSpan() {
|
||||
Instant::Instant() {
|
||||
_dbg_assert_(frequencyMult != 0.0);
|
||||
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *>(&nativeStart_));
|
||||
}
|
||||
|
||||
double TimeSpan::ElapsedSeconds() const {
|
||||
double Instant::ElapsedSeconds() const {
|
||||
LARGE_INTEGER time;
|
||||
QueryPerformanceCounter(&time);
|
||||
double elapsed = static_cast<double>(time.QuadPart - nativeStart_);
|
||||
return elapsed * frequencyMult;
|
||||
}
|
||||
|
||||
int64_t TimeSpan::ElapsedNanos() const {
|
||||
int64_t Instant::ElapsedNanos() const {
|
||||
return (int64_t)(ElapsedSeconds() * 1000000000.0);
|
||||
}
|
||||
|
||||
#elif PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(LINUX) || PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)
|
||||
|
||||
void TimeInit() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
// The only intended use is to match the timings in VK_GOOGLE_display_timing
|
||||
uint64_t time_now_raw() {
|
||||
struct timespec tp;
|
||||
@ -174,6 +185,10 @@ double TimeSpan::ElapsedSeconds() const {
|
||||
|
||||
#else
|
||||
|
||||
void TimeInit() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
static time_t start;
|
||||
|
||||
double time_now_d() {
|
||||
@ -229,7 +244,7 @@ int64_t TimeSpan::ElapsedNanos() const {
|
||||
}
|
||||
|
||||
double TimeSpan::ElapsedSeconds() const {
|
||||
return (double)ElapsedNanos() * (1.0 / 1000000000);
|
||||
return (double)ElapsedNanos() * (1.0 / 1000000000.0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -246,6 +261,53 @@ void sleep_ms(int ms) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Precise Windows sleep function from: https://github.com/blat-blatnik/Snippets/blob/main/precise_sleep.c
|
||||
// Described in: https://blog.bearcats.nl/perfect-sleep-function/
|
||||
|
||||
void sleep_precise(double seconds) {
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER qpc;
|
||||
QueryPerformanceCounter(&qpc);
|
||||
INT64 targetQpc = (INT64)(qpc.QuadPart + seconds * QpcPerSecond);
|
||||
if (Timer) { // Try using a high resolution timer first.
|
||||
const double TOLERANCE = 0.001'02;
|
||||
INT64 maxTicks = (INT64)SchedulerPeriodMs * 9'500;
|
||||
for (;;) // Break sleep up into parts that are lower than scheduler period.
|
||||
{
|
||||
double remainingSeconds = (targetQpc - qpc.QuadPart) / (double)QpcPerSecond;
|
||||
INT64 sleepTicks = (INT64)((remainingSeconds - TOLERANCE) * 10'000'000);
|
||||
if (sleepTicks <= 0)
|
||||
break;
|
||||
LARGE_INTEGER due;
|
||||
due.QuadPart = -(sleepTicks > maxTicks ? maxTicks : sleepTicks);
|
||||
SetWaitableTimerEx(Timer, &due, 0, NULL, NULL, NULL, 0);
|
||||
WaitForSingleObject(Timer, INFINITE);
|
||||
QueryPerformanceCounter(&qpc);
|
||||
}
|
||||
} else { // Fallback to Sleep.
|
||||
const double TOLERANCE = 0.000'02;
|
||||
double sleepMs = (seconds - TOLERANCE) * 1000 - SchedulerPeriodMs; // Sleep for 1 scheduler period less than requested.
|
||||
int sleepSlices = (int)(sleepMs / SchedulerPeriodMs);
|
||||
if (sleepSlices > 0)
|
||||
Sleep((DWORD)sleepSlices * SchedulerPeriodMs);
|
||||
QueryPerformanceCounter(&qpc);
|
||||
}
|
||||
while (qpc.QuadPart < targetQpc) // Spin for any remaining time.
|
||||
{
|
||||
YieldProcessor();
|
||||
QueryPerformanceCounter(&qpc);
|
||||
}
|
||||
#else
|
||||
#if defined(HAVE_LIBNX)
|
||||
svcSleepThread((int64_t)(seconds * 1000000000.0));
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
emscripten_sleep(seconds * 1000.0);
|
||||
#else
|
||||
usleep(seconds * 1000000.0);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return the current time formatted as Minutes:Seconds:Milliseconds
|
||||
// in the form 00:00:000.
|
||||
void GetCurrentTimeFormatted(char formattedTime[13]) {
|
||||
|
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
void TimeInit();
|
||||
|
||||
// Seconds.
|
||||
double time_now_d();
|
||||
|
||||
@ -18,32 +20,24 @@ double time_now_unix_utc();
|
||||
// Sleep. Does not necessarily have millisecond granularity, especially on Windows.
|
||||
void sleep_ms(int ms);
|
||||
|
||||
// Precise sleep. Can consume a little bit of CPU on Windows at least.
|
||||
void sleep_precise(double seconds);
|
||||
|
||||
// Yield. Signals that this thread is busy-waiting but wants to allow other hyperthreads to run.
|
||||
void yield();
|
||||
|
||||
void GetCurrentTimeFormatted(char formattedTime[13]);
|
||||
|
||||
// Rust-style Instant for clear and easy timing.
|
||||
// Most accurate timer possible - no extra double conversions. Only for spans.
|
||||
class Instant {
|
||||
public:
|
||||
static Instant Now() {
|
||||
return Instant(time_now_d());
|
||||
return Instant();
|
||||
}
|
||||
double ElapsedSeconds() const {
|
||||
return time_now_d() - instantTime_;
|
||||
}
|
||||
private:
|
||||
explicit Instant(double initTime) : instantTime_(initTime) {}
|
||||
double instantTime_;
|
||||
};
|
||||
|
||||
// Most accurate timer possible - no extra double conversions. Only for spans.
|
||||
class TimeSpan {
|
||||
public:
|
||||
TimeSpan();
|
||||
double ElapsedSeconds() const;
|
||||
int64_t ElapsedNanos() const;
|
||||
private:
|
||||
Instant();
|
||||
uint64_t nativeStart_;
|
||||
#ifndef _WIN32
|
||||
int64_t nsecs_;
|
||||
|
@ -285,9 +285,9 @@ void IRJit::RunLoopUntil(u64 globalticks) {
|
||||
instPtr++;
|
||||
#ifdef IR_PROFILING
|
||||
IRBlock *block = blocks_.GetBlock(blocks_.GetBlockNumFromOffset(offset));
|
||||
TimeSpan span;
|
||||
Instant start = Instant::Now();
|
||||
mips->pc = IRInterpret(mips, instPtr);
|
||||
int64_t elapsedNanos = span.ElapsedNanos();
|
||||
int64_t elapsedNanos = start.ElapsedNanos();
|
||||
block->profileStats_.executions += 1;
|
||||
block->profileStats_.totalNanos += elapsedNanos;
|
||||
#else
|
||||
|
@ -56,7 +56,7 @@
|
||||
#include "Core/HLE/sceKernelModule.h"
|
||||
#include "Core/HLE/sceKernelMemory.h"
|
||||
|
||||
static std::thread loadingThread;
|
||||
static std::thread g_loadingThread;
|
||||
|
||||
static void UseLargeMem(int memsize) {
|
||||
if (memsize != 1) {
|
||||
@ -313,7 +313,7 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) {
|
||||
// Note: this thread reads the game binary, loads caches, and links HLE while UI spins.
|
||||
// To do something deterministically when the game starts, disabling this thread won't be enough.
|
||||
// Instead: Use Core_ListenLifecycle() or watch coreState.
|
||||
loadingThread = std::thread([bootpath] {
|
||||
g_loadingThread = std::thread([bootpath] {
|
||||
SetCurrentThreadName("ExecLoader");
|
||||
PSP_LoadingLock guard;
|
||||
if (coreState != CORE_POWERUP)
|
||||
@ -474,7 +474,7 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) {
|
||||
|
||||
PSPLoaders_Shutdown();
|
||||
// Note: See Load_PSP_ISO for notes about this thread.
|
||||
loadingThread = std::thread([finalName] {
|
||||
g_loadingThread = std::thread([finalName] {
|
||||
SetCurrentThreadName("ExecLoader");
|
||||
PSP_LoadingLock guard;
|
||||
if (coreState != CORE_POWERUP)
|
||||
@ -500,7 +500,7 @@ bool Load_PSP_GE_Dump(FileLoader *fileLoader, std::string *error_string) {
|
||||
|
||||
PSPLoaders_Shutdown();
|
||||
// Note: See Load_PSP_ISO for notes about this thread.
|
||||
loadingThread = std::thread([] {
|
||||
g_loadingThread = std::thread([] {
|
||||
SetCurrentThreadName("ExecLoader");
|
||||
PSP_LoadingLock guard;
|
||||
if (coreState != CORE_POWERUP)
|
||||
@ -521,6 +521,6 @@ bool Load_PSP_GE_Dump(FileLoader *fileLoader, std::string *error_string) {
|
||||
}
|
||||
|
||||
void PSPLoaders_Shutdown() {
|
||||
if (loadingThread.joinable())
|
||||
loadingThread.join();
|
||||
if (g_loadingThread.joinable())
|
||||
g_loadingThread.join();
|
||||
}
|
||||
|
@ -45,6 +45,8 @@
|
||||
#include "Common/Thread/ThreadUtil.h"
|
||||
#include "Common/Data/Encoding/Utf8.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Common/TimeUtil.h"
|
||||
|
||||
#include "Core/Config.h"
|
||||
#include "Core/ConfigValues.h"
|
||||
#include "Core/HW/Camera.h"
|
||||
@ -799,6 +801,8 @@ Q_DECL_EXPORT
|
||||
#endif
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
TimeInit();
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "--version")) {
|
||||
printf("%s\n", PPSSPP_GIT_VERSION);
|
||||
|
@ -1108,6 +1108,8 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
TimeInit();
|
||||
|
||||
#ifdef HAVE_LIBNX
|
||||
socketInitializeDefault();
|
||||
nxlinkStdio();
|
||||
|
@ -887,6 +887,8 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin
|
||||
|
||||
SetCurrentThreadName("Main");
|
||||
|
||||
TimeInit();
|
||||
|
||||
WinMainInit();
|
||||
|
||||
#ifndef _DEBUG
|
||||
|
@ -294,6 +294,8 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) {
|
||||
"(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
|
||||
RegisterAttachDetach(&Android_AttachThreadToJNI, &Android_DetachThreadFromJNI);
|
||||
|
||||
TimeInit();
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
|
@ -1195,6 +1195,8 @@ static const struct retro_controller_info ports[] =
|
||||
|
||||
void retro_init(void)
|
||||
{
|
||||
TimeInit();
|
||||
|
||||
struct retro_log_callback log;
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user