diff options
| author | 2023-03-01 21:06:19 -0500 | |
|---|---|---|
| committer | 2023-03-05 02:36:31 -0500 | |
| commit | bff14532825e7517882ca913738347059f73cf7f (patch) | |
| tree | f21f509d4c7458a79e2fc399c1de3036a7efd51d | |
| parent | main: (Windows) Set the current timer resolution to the maximum (diff) | |
| download | yuzu-bff14532825e7517882ca913738347059f73cf7f.tar.gz yuzu-bff14532825e7517882ca913738347059f73cf7f.tar.xz yuzu-bff14532825e7517882ca913738347059f73cf7f.zip | |
core_timing: Use higher precision sleeps on Windows
The precision of sleep_for and wait_for is limited to 1-1.5ms on Windows.
Using SleepForOneTick() allows us to sleep for exactly one interval of the current timer resolution.
This allows us to take advantage of systems that have a timer resolution of 0.5ms to reduce CPU overhead in the event loop.
Diffstat (limited to '')
| -rw-r--r-- | src/common/wall_clock.cpp | 5 | ||||
| -rw-r--r-- | src/common/wall_clock.h | 3 | ||||
| -rw-r--r-- | src/core/core_timing.cpp | 55 | ||||
| -rw-r--r-- | src/core/core_timing.h | 6 | ||||
| -rw-r--r-- | src/video_core/gpu.cpp | 2 |
5 files changed, 47 insertions, 24 deletions
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index 6d972d136..817e71d52 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp | |||
| @@ -81,4 +81,9 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, | |||
| 81 | 81 | ||
| 82 | #endif | 82 | #endif |
| 83 | 83 | ||
| 84 | std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency, | ||
| 85 | u64 emulated_clock_frequency) { | ||
| 86 | return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency); | ||
| 87 | } | ||
| 88 | |||
| 84 | } // namespace Common | 89 | } // namespace Common |
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index 828a523a8..157ec5eae 100644 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h | |||
| @@ -55,4 +55,7 @@ private: | |||
| 55 | [[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, | 55 | [[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, |
| 56 | u64 emulated_clock_frequency); | 56 | u64 emulated_clock_frequency); |
| 57 | 57 | ||
| 58 | [[nodiscard]] std::unique_ptr<WallClock> CreateStandardWallClock(u64 emulated_cpu_frequency, | ||
| 59 | u64 emulated_clock_frequency); | ||
| 60 | |||
| 58 | } // namespace Common | 61 | } // namespace Common |
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 3a63b52e3..742cfb996 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -6,6 +6,10 @@ | |||
| 6 | #include <string> | 6 | #include <string> |
| 7 | #include <tuple> | 7 | #include <tuple> |
| 8 | 8 | ||
| 9 | #ifdef _WIN32 | ||
| 10 | #include "common/windows/timer_resolution.h" | ||
| 11 | #endif | ||
| 12 | |||
| 9 | #include "common/microprofile.h" | 13 | #include "common/microprofile.h" |
| 10 | #include "core/core_timing.h" | 14 | #include "core/core_timing.h" |
| 11 | #include "core/core_timing_util.h" | 15 | #include "core/core_timing_util.h" |
| @@ -38,7 +42,8 @@ struct CoreTiming::Event { | |||
| 38 | }; | 42 | }; |
| 39 | 43 | ||
| 40 | CoreTiming::CoreTiming() | 44 | CoreTiming::CoreTiming() |
| 41 | : clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {} | 45 | : cpu_clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)}, |
| 46 | event_clock{Common::CreateStandardWallClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {} | ||
| 42 | 47 | ||
| 43 | CoreTiming::~CoreTiming() { | 48 | CoreTiming::~CoreTiming() { |
| 44 | Reset(); | 49 | Reset(); |
| @@ -185,15 +190,15 @@ void CoreTiming::ResetTicks() { | |||
| 185 | } | 190 | } |
| 186 | 191 | ||
| 187 | u64 CoreTiming::GetCPUTicks() const { | 192 | u64 CoreTiming::GetCPUTicks() const { |
| 188 | if (is_multicore) { | 193 | if (is_multicore) [[likely]] { |
| 189 | return clock->GetCPUCycles(); | 194 | return cpu_clock->GetCPUCycles(); |
| 190 | } | 195 | } |
| 191 | return ticks; | 196 | return ticks; |
| 192 | } | 197 | } |
| 193 | 198 | ||
| 194 | u64 CoreTiming::GetClockTicks() const { | 199 | u64 CoreTiming::GetClockTicks() const { |
| 195 | if (is_multicore) { | 200 | if (is_multicore) [[likely]] { |
| 196 | return clock->GetClockCycles(); | 201 | return cpu_clock->GetClockCycles(); |
| 197 | } | 202 | } |
| 198 | return CpuCyclesToClockCycles(ticks); | 203 | return CpuCyclesToClockCycles(ticks); |
| 199 | } | 204 | } |
| @@ -252,21 +257,20 @@ void CoreTiming::ThreadLoop() { | |||
| 252 | const auto next_time = Advance(); | 257 | const auto next_time = Advance(); |
| 253 | if (next_time) { | 258 | if (next_time) { |
| 254 | // There are more events left in the queue, wait until the next event. | 259 | // There are more events left in the queue, wait until the next event. |
| 255 | const auto wait_time = *next_time - GetGlobalTimeNs().count(); | 260 | auto wait_time = *next_time - GetGlobalTimeNs().count(); |
| 256 | if (wait_time > 0) { | 261 | if (wait_time > 0) { |
| 257 | #ifdef _WIN32 | 262 | #ifdef _WIN32 |
| 258 | // Assume a timer resolution of 1ms. | 263 | const auto timer_resolution_ns = |
| 259 | static constexpr s64 TimerResolutionNS = 1000000; | 264 | Common::Windows::GetCurrentTimerResolution().count(); |
| 260 | 265 | ||
| 261 | // Sleep in discrete intervals of the timer resolution, and spin the rest. | 266 | while (!paused && !event.IsSet() && wait_time > 0) { |
| 262 | const auto sleep_time = wait_time - (wait_time % TimerResolutionNS); | 267 | wait_time = *next_time - GetGlobalTimeNs().count(); |
| 263 | if (sleep_time > 0) { | ||
| 264 | event.WaitFor(std::chrono::nanoseconds(sleep_time)); | ||
| 265 | } | ||
| 266 | 268 | ||
| 267 | while (!paused && !event.IsSet() && GetGlobalTimeNs().count() < *next_time) { | 269 | if (wait_time >= timer_resolution_ns) { |
| 268 | // Yield to reduce thread starvation. | 270 | Common::Windows::SleepForOneTick(); |
| 269 | std::this_thread::yield(); | 271 | } else { |
| 272 | std::this_thread::yield(); | ||
| 273 | } | ||
| 270 | } | 274 | } |
| 271 | 275 | ||
| 272 | if (event.IsSet()) { | 276 | if (event.IsSet()) { |
| @@ -285,9 +289,9 @@ void CoreTiming::ThreadLoop() { | |||
| 285 | } | 289 | } |
| 286 | 290 | ||
| 287 | paused_set = true; | 291 | paused_set = true; |
| 288 | clock->Pause(true); | 292 | event_clock->Pause(true); |
| 289 | pause_event.Wait(); | 293 | pause_event.Wait(); |
| 290 | clock->Pause(false); | 294 | event_clock->Pause(false); |
| 291 | } | 295 | } |
| 292 | } | 296 | } |
| 293 | 297 | ||
| @@ -303,16 +307,23 @@ void CoreTiming::Reset() { | |||
| 303 | has_started = false; | 307 | has_started = false; |
| 304 | } | 308 | } |
| 305 | 309 | ||
| 310 | std::chrono::nanoseconds CoreTiming::GetCPUTimeNs() const { | ||
| 311 | if (is_multicore) [[likely]] { | ||
| 312 | return cpu_clock->GetTimeNS(); | ||
| 313 | } | ||
| 314 | return CyclesToNs(ticks); | ||
| 315 | } | ||
| 316 | |||
| 306 | std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { | 317 | std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const { |
| 307 | if (is_multicore) { | 318 | if (is_multicore) [[likely]] { |
| 308 | return clock->GetTimeNS(); | 319 | return event_clock->GetTimeNS(); |
| 309 | } | 320 | } |
| 310 | return CyclesToNs(ticks); | 321 | return CyclesToNs(ticks); |
| 311 | } | 322 | } |
| 312 | 323 | ||
| 313 | std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { | 324 | std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { |
| 314 | if (is_multicore) { | 325 | if (is_multicore) [[likely]] { |
| 315 | return clock->GetTimeUS(); | 326 | return event_clock->GetTimeUS(); |
| 316 | } | 327 | } |
| 317 | return CyclesToUs(ticks); | 328 | return CyclesToUs(ticks); |
| 318 | } | 329 | } |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index da366637b..4b89c0c39 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -122,6 +122,9 @@ public: | |||
| 122 | /// Returns current time in emulated in Clock cycles | 122 | /// Returns current time in emulated in Clock cycles |
| 123 | u64 GetClockTicks() const; | 123 | u64 GetClockTicks() const; |
| 124 | 124 | ||
| 125 | /// Returns current time in nanoseconds. | ||
| 126 | std::chrono::nanoseconds GetCPUTimeNs() const; | ||
| 127 | |||
| 125 | /// Returns current time in microseconds. | 128 | /// Returns current time in microseconds. |
| 126 | std::chrono::microseconds GetGlobalTimeUs() const; | 129 | std::chrono::microseconds GetGlobalTimeUs() const; |
| 127 | 130 | ||
| @@ -139,7 +142,8 @@ private: | |||
| 139 | 142 | ||
| 140 | void Reset(); | 143 | void Reset(); |
| 141 | 144 | ||
| 142 | std::unique_ptr<Common::WallClock> clock; | 145 | std::unique_ptr<Common::WallClock> cpu_clock; |
| 146 | std::unique_ptr<Common::WallClock> event_clock; | ||
| 143 | 147 | ||
| 144 | s64 global_timer = 0; | 148 | s64 global_timer = 0; |
| 145 | 149 | ||
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 7024a19cf..2e7f9c5ed 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -197,7 +197,7 @@ struct GPU::Impl { | |||
| 197 | constexpr u64 gpu_ticks_num = 384; | 197 | constexpr u64 gpu_ticks_num = 384; |
| 198 | constexpr u64 gpu_ticks_den = 625; | 198 | constexpr u64 gpu_ticks_den = 625; |
| 199 | 199 | ||
| 200 | u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count(); | 200 | u64 nanoseconds = system.CoreTiming().GetCPUTimeNs().count(); |
| 201 | if (Settings::values.use_fast_gpu_time.GetValue()) { | 201 | if (Settings::values.use_fast_gpu_time.GetValue()) { |
| 202 | nanoseconds /= 256; | 202 | nanoseconds /= 256; |
| 203 | } | 203 | } |