Browse Source
Merge pull request #9889 from Morph1984/time-is-ticking
Merge pull request #9889 from Morph1984/time-is-ticking
core_timing: Reduce CPU usage on Windowsnce_cpp
committed by
GitHub
16 changed files with 324 additions and 65 deletions
-
4CMakeLists.txt
-
6dist/yuzu.manifest
-
10src/common/CMakeLists.txt
-
56src/common/steady_clock.cpp
-
23src/common/steady_clock.h
-
39src/common/wall_clock.cpp
-
3src/common/wall_clock.h
-
109src/common/windows/timer_resolution.cpp
-
38src/common/windows/timer_resolution.h
-
17src/common/x64/native_clock.cpp
-
55src/core/core_timing.cpp
-
6src/core/core_timing.h
-
8src/core/hardware_properties.h
-
2src/video_core/gpu.cpp
-
9src/yuzu/main.cpp
-
4src/yuzu_cmd/yuzu.cpp
@ -0,0 +1,56 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|||
|
|||
#if defined(_WIN32)
|
|||
#include <windows.h>
|
|||
#else
|
|||
#include <time.h>
|
|||
#endif
|
|||
|
|||
#include "common/steady_clock.h"
|
|||
|
|||
namespace Common { |
|||
|
|||
#ifdef _WIN32
|
|||
static s64 WindowsQueryPerformanceFrequency() { |
|||
LARGE_INTEGER frequency; |
|||
QueryPerformanceFrequency(&frequency); |
|||
return frequency.QuadPart; |
|||
} |
|||
|
|||
static s64 WindowsQueryPerformanceCounter() { |
|||
LARGE_INTEGER counter; |
|||
QueryPerformanceCounter(&counter); |
|||
return counter.QuadPart; |
|||
} |
|||
#endif
|
|||
|
|||
SteadyClock::time_point SteadyClock::Now() noexcept { |
|||
#if defined(_WIN32)
|
|||
static const auto freq = WindowsQueryPerformanceFrequency(); |
|||
const auto counter = WindowsQueryPerformanceCounter(); |
|||
|
|||
// 10 MHz is a very common QPC frequency on modern PCs.
|
|||
// Optimizing for this specific frequency can double the performance of
|
|||
// this function by avoiding the expensive frequency conversion path.
|
|||
static constexpr s64 TenMHz = 10'000'000; |
|||
|
|||
if (freq == TenMHz) [[likely]] { |
|||
static_assert(period::den % TenMHz == 0); |
|||
static constexpr s64 Multiplier = period::den / TenMHz; |
|||
return time_point{duration{counter * Multiplier}}; |
|||
} |
|||
|
|||
const auto whole = (counter / freq) * period::den; |
|||
const auto part = (counter % freq) * period::den / freq; |
|||
return time_point{duration{whole + part}}; |
|||
#elif defined(__APPLE__)
|
|||
return time_point{duration{clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW)}}; |
|||
#else
|
|||
timespec ts; |
|||
clock_gettime(CLOCK_MONOTONIC, &ts); |
|||
return time_point{std::chrono::seconds{ts.tv_sec} + std::chrono::nanoseconds{ts.tv_nsec}}; |
|||
#endif
|
|||
} |
|||
|
|||
}; // namespace Common
|
|||
@ -0,0 +1,23 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <chrono> |
|||
|
|||
#include "common/common_types.h" |
|||
|
|||
namespace Common { |
|||
|
|||
struct SteadyClock { |
|||
using rep = s64; |
|||
using period = std::nano; |
|||
using duration = std::chrono::nanoseconds; |
|||
using time_point = std::chrono::time_point<SteadyClock>; |
|||
|
|||
static constexpr bool is_steady = true; |
|||
|
|||
[[nodiscard]] static time_point Now() noexcept; |
|||
}; |
|||
|
|||
} // namespace Common |
|||
@ -0,0 +1,109 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|||
|
|||
#include <windows.h>
|
|||
|
|||
#include "common/windows/timer_resolution.h"
|
|||
|
|||
extern "C" { |
|||
// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtQueryTimerResolution.html
|
|||
NTSYSAPI LONG NTAPI NtQueryTimerResolution(PULONG MinimumResolution, PULONG MaximumResolution, |
|||
PULONG CurrentResolution); |
|||
|
|||
// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FTime%2FNtSetTimerResolution.html
|
|||
NTSYSAPI LONG NTAPI NtSetTimerResolution(ULONG DesiredResolution, BOOLEAN SetResolution, |
|||
PULONG CurrentResolution); |
|||
|
|||
// http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FThread%2FNtDelayExecution.html
|
|||
NTSYSAPI LONG NTAPI NtDelayExecution(BOOLEAN Alertable, PLARGE_INTEGER DelayInterval); |
|||
} |
|||
|
|||
// Defines for compatibility with older Windows 10 SDKs.
|
|||
|
|||
#ifndef PROCESS_POWER_THROTTLING_EXECUTION_SPEED
|
|||
#define PROCESS_POWER_THROTTLING_EXECUTION_SPEED 0x1
|
|||
#endif
|
|||
#ifndef PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION
|
|||
#define PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION 0x4
|
|||
#endif
|
|||
|
|||
namespace Common::Windows { |
|||
|
|||
namespace { |
|||
|
|||
using namespace std::chrono; |
|||
|
|||
constexpr nanoseconds ToNS(ULONG hundred_ns) { |
|||
return nanoseconds{hundred_ns * 100}; |
|||
} |
|||
|
|||
constexpr ULONG ToHundredNS(nanoseconds ns) { |
|||
return static_cast<ULONG>(ns.count()) / 100; |
|||
} |
|||
|
|||
struct TimerResolution { |
|||
std::chrono::nanoseconds minimum; |
|||
std::chrono::nanoseconds maximum; |
|||
std::chrono::nanoseconds current; |
|||
}; |
|||
|
|||
TimerResolution GetTimerResolution() { |
|||
ULONG MinimumTimerResolution; |
|||
ULONG MaximumTimerResolution; |
|||
ULONG CurrentTimerResolution; |
|||
NtQueryTimerResolution(&MinimumTimerResolution, &MaximumTimerResolution, |
|||
&CurrentTimerResolution); |
|||
return { |
|||
.minimum{ToNS(MinimumTimerResolution)}, |
|||
.maximum{ToNS(MaximumTimerResolution)}, |
|||
.current{ToNS(CurrentTimerResolution)}, |
|||
}; |
|||
} |
|||
|
|||
void SetHighQoS() { |
|||
// https://learn.microsoft.com/en-us/windows/win32/procthread/quality-of-service
|
|||
PROCESS_POWER_THROTTLING_STATE PowerThrottling{ |
|||
.Version{PROCESS_POWER_THROTTLING_CURRENT_VERSION}, |
|||
.ControlMask{PROCESS_POWER_THROTTLING_EXECUTION_SPEED | |
|||
PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION}, |
|||
.StateMask{}, |
|||
}; |
|||
SetProcessInformation(GetCurrentProcess(), ProcessPowerThrottling, &PowerThrottling, |
|||
sizeof(PROCESS_POWER_THROTTLING_STATE)); |
|||
} |
|||
|
|||
} // Anonymous namespace
|
|||
|
|||
nanoseconds GetMinimumTimerResolution() { |
|||
return GetTimerResolution().minimum; |
|||
} |
|||
|
|||
nanoseconds GetMaximumTimerResolution() { |
|||
return GetTimerResolution().maximum; |
|||
} |
|||
|
|||
nanoseconds GetCurrentTimerResolution() { |
|||
return GetTimerResolution().current; |
|||
} |
|||
|
|||
nanoseconds SetCurrentTimerResolution(nanoseconds timer_resolution) { |
|||
// Set the timer resolution, and return the current timer resolution.
|
|||
const auto DesiredTimerResolution = ToHundredNS(timer_resolution); |
|||
ULONG CurrentTimerResolution; |
|||
NtSetTimerResolution(DesiredTimerResolution, TRUE, &CurrentTimerResolution); |
|||
return ToNS(CurrentTimerResolution); |
|||
} |
|||
|
|||
nanoseconds SetCurrentTimerResolutionToMaximum() { |
|||
SetHighQoS(); |
|||
return SetCurrentTimerResolution(GetMaximumTimerResolution()); |
|||
} |
|||
|
|||
void SleepForOneTick() { |
|||
LARGE_INTEGER DelayInterval{ |
|||
.QuadPart{-1}, |
|||
}; |
|||
NtDelayExecution(FALSE, &DelayInterval); |
|||
} |
|||
|
|||
} // namespace Common::Windows
|
|||
@ -0,0 +1,38 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <chrono> |
|||
|
|||
namespace Common::Windows { |
|||
|
|||
/// Returns the minimum (least precise) supported timer resolution in nanoseconds. |
|||
std::chrono::nanoseconds GetMinimumTimerResolution(); |
|||
|
|||
/// Returns the maximum (most precise) supported timer resolution in nanoseconds. |
|||
std::chrono::nanoseconds GetMaximumTimerResolution(); |
|||
|
|||
/// Returns the current timer resolution in nanoseconds. |
|||
std::chrono::nanoseconds GetCurrentTimerResolution(); |
|||
|
|||
/** |
|||
* Sets the current timer resolution. |
|||
* |
|||
* @param timer_resolution Timer resolution in nanoseconds. |
|||
* |
|||
* @returns The current timer resolution. |
|||
*/ |
|||
std::chrono::nanoseconds SetCurrentTimerResolution(std::chrono::nanoseconds timer_resolution); |
|||
|
|||
/** |
|||
* Sets the current timer resolution to the maximum supported timer resolution. |
|||
* |
|||
* @returns The current timer resolution. |
|||
*/ |
|||
std::chrono::nanoseconds SetCurrentTimerResolutionToMaximum(); |
|||
|
|||
/// Sleep for one tick of the current timer resolution. |
|||
void SleepForOneTick(); |
|||
|
|||
} // namespace Common::Windows |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue