mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-02-01 17:52:13 +01:00
126 lines
3.2 KiB
C++
126 lines
3.2 KiB
C++
#include "util_sleep.h"
|
|
#include "util_string.h"
|
|
|
|
#include "./log/log.h"
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
namespace dxvk {
|
|
|
|
Sleep Sleep::s_instance;
|
|
|
|
|
|
Sleep::Sleep() {
|
|
|
|
}
|
|
|
|
|
|
Sleep::~Sleep() {
|
|
|
|
}
|
|
|
|
|
|
void Sleep::initialize() {
|
|
std::lock_guard lock(m_mutex);
|
|
|
|
if (m_initialized.load())
|
|
return;
|
|
|
|
initializePlatformSpecifics();
|
|
m_sleepThreshold = 4 * m_sleepGranularity;
|
|
|
|
m_initialized.store(true, std::memory_order_release);
|
|
}
|
|
|
|
|
|
void Sleep::initializePlatformSpecifics() {
|
|
#ifdef _WIN32
|
|
HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll");
|
|
|
|
if (ntdll) {
|
|
NtDelayExecution = reinterpret_cast<NtDelayExecutionProc>(
|
|
::GetProcAddress(ntdll, "NtDelayExecution"));
|
|
auto NtQueryTimerResolution = reinterpret_cast<NtQueryTimerResolutionProc>(
|
|
::GetProcAddress(ntdll, "NtQueryTimerResolution"));
|
|
auto NtSetTimerResolution = reinterpret_cast<NtSetTimerResolutionProc>(
|
|
::GetProcAddress(ntdll, "NtSetTimerResolution"));
|
|
|
|
ULONG min, max, cur;
|
|
|
|
// Wine's implementation of these functions is a stub as of 6.10, which is fine
|
|
// since it uses select() in NtDelayExecution. This is only relevant for Windows.
|
|
if (NtQueryTimerResolution && !NtQueryTimerResolution(&min, &max, &cur)) {
|
|
m_sleepGranularity = TimerDuration(cur);
|
|
|
|
if (NtSetTimerResolution && !NtSetTimerResolution(max, TRUE, &cur)) {
|
|
Logger::info(str::format("Setting timer interval to ", (double(max) / 10.0), " us"));
|
|
m_sleepGranularity = TimerDuration(max);
|
|
}
|
|
}
|
|
} else {
|
|
// Assume 1ms sleep granularity by default
|
|
m_sleepGranularity = TimerDuration(1ms);
|
|
}
|
|
#else
|
|
// Assume 0.5ms sleep granularity by default
|
|
m_sleepGranularity = TimerDuration(500us);
|
|
#endif
|
|
}
|
|
|
|
|
|
Sleep::TimePoint Sleep::sleep(TimePoint t0, TimerDuration duration) {
|
|
if (duration <= TimerDuration::zero())
|
|
return t0;
|
|
|
|
// If necessary, initialize function pointers and some values
|
|
if (!m_initialized.load(std::memory_order_acquire))
|
|
initialize();
|
|
|
|
// Busy-wait for the last couple of milliseconds since sleeping
|
|
// on Windows is highly inaccurate and inconsistent.
|
|
TimerDuration sleepThreshold = m_sleepThreshold;
|
|
|
|
if (m_sleepGranularity != TimerDuration::zero())
|
|
sleepThreshold += duration / 6;
|
|
|
|
TimerDuration remaining = duration;
|
|
TimePoint t1 = t0;
|
|
|
|
while (remaining > sleepThreshold) {
|
|
TimerDuration sleepDuration = remaining - sleepThreshold;
|
|
|
|
systemSleep(sleepDuration);
|
|
|
|
t1 = dxvk::high_resolution_clock::now();
|
|
remaining -= std::chrono::duration_cast<TimerDuration>(t1 - t0);
|
|
t0 = t1;
|
|
}
|
|
|
|
// Busy-wait until we have slept long enough
|
|
while (remaining > TimerDuration::zero()) {
|
|
t1 = dxvk::high_resolution_clock::now();
|
|
remaining -= std::chrono::duration_cast<TimerDuration>(t1 - t0);
|
|
t0 = t1;
|
|
}
|
|
|
|
return t1;
|
|
}
|
|
|
|
|
|
void Sleep::systemSleep(TimerDuration duration) {
|
|
#ifdef _WIN32
|
|
if (NtDelayExecution) {
|
|
LARGE_INTEGER ticks;
|
|
ticks.QuadPart = -duration.count();
|
|
|
|
NtDelayExecution(FALSE, &ticks);
|
|
} else {
|
|
std::this_thread::sleep_for(duration);
|
|
}
|
|
#else
|
|
std::this_thread::sleep_for(duration);
|
|
#endif
|
|
}
|
|
|
|
}
|