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
-
53src/core/core_timing.cpp
-
6src/core/core_timing.h
-
6src/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