diff --git a/src/recomp/cont.cpp b/src/recomp/cont.cpp index 8e0c18f..c188fa0 100644 --- a/src/recomp/cont.cpp +++ b/src/recomp/cont.cpp @@ -3,10 +3,10 @@ static ultramodern::input_callbacks_t input_callbacks; -std::chrono::system_clock::time_point input_poll_time; +std::chrono::high_resolution_clock::time_point input_poll_time; void update_poll_time() { - input_poll_time = std::chrono::system_clock::now(); + input_poll_time = std::chrono::high_resolution_clock::now(); } extern "C" void recomp_set_current_frame_poll_id(uint8_t* rdram, recomp_context* ctx) { @@ -18,7 +18,7 @@ extern "C" void recomp_measure_latency(uint8_t* rdram, recomp_context* ctx) { } void ultramodern::measure_input_latency() { - // printf("Delta: %ld micros\n", std::chrono::duration_cast(std::chrono::system_clock::now() - input_poll_time)); + // printf("Delta: %ld micros\n", std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - input_poll_time)); } void set_input_callbacks(const ultramodern::input_callbacks_t& callbacks) { diff --git a/src/recomp/recomp.cpp b/src/recomp/recomp.cpp index 666857b..10ac659 100644 --- a/src/recomp/recomp.cpp +++ b/src/recomp/recomp.cpp @@ -429,8 +429,7 @@ void recomp::start(ultramodern::WindowHandle window_handle, const ultramodern::a }, window_handle, rdram_buffer.get()}; while (!exited) { - using namespace std::chrono_literals; - std::this_thread::sleep_for(1ms); + ultramodern::sleep_milliseconds(1); if (gfx_callbacks.update_gfx != nullptr) { gfx_callbacks.update_gfx(gfx_data); } diff --git a/src/ui/ui_config.cpp b/src/ui/ui_config.cpp index 28825ea..2cc183d 100644 --- a/src/ui/ui_config.cpp +++ b/src/ui/ui_config.cpp @@ -322,10 +322,7 @@ public: throw std::runtime_error("Failed to make RmlUi data model for the graphics config menu"); } - { - using namespace std::chrono_literals; - std::this_thread::sleep_for(50ms); - } + ultramodern::sleep_milliseconds(50); new_options = ultramodern::get_graphics_config(); bind_config_list_events(constructor); diff --git a/ultramodern/events.cpp b/ultramodern/events.cpp index 857bc43..8b17981 100644 --- a/ultramodern/events.cpp +++ b/ultramodern/events.cpp @@ -115,15 +115,20 @@ void vi_thread_func() { while (!exited) { // Determine the next VI time (more accurate than adding 16ms each VI interrupt) auto next = ultramodern::get_start() + (total_vis * 1000000us) / (60 * ultramodern::get_speed_multiplier()); - //if (next > std::chrono::system_clock::now()) { + //if (next > std::chrono::high_resolution_clock::now()) { // printf("Sleeping for %" PRIu64 " us to get from %" PRIu64 " us to %" PRIu64 " us \n", - // (next - std::chrono::system_clock::now()) / 1us, - // (std::chrono::system_clock::now() - events_context.start) / 1us, + // (next - std::chrono::high_resolution_clock::now()) / 1us, + // (std::chrono::high_resolution_clock::now() - events_context.start) / 1us, // (next - events_context.start) / 1us); //} else { // printf("No need to sleep\n"); //} - std::this_thread::sleep_until(next); + // Detect if there's more than a second to wait and wait a fixed amount instead for the next VI if so, as that usually means the system clock went back in time. + if (std::chrono::floor(next - std::chrono::high_resolution_clock::now()) > 1s) { + // printf("Skipping the next VI wait\n"); + next = std::chrono::high_resolution_clock::now(); + } + ultramodern::sleep_until(next); // Calculate how many VIs have passed uint64_t new_total_vis = (ultramodern::time_since_start() * (60 * ultramodern::get_speed_multiplier()) / 1000ms) + 1; if (new_total_vis > total_vis + 1) { @@ -325,9 +330,9 @@ void gfx_thread_func(uint8_t* rdram, std::atomic_flag* thread_ready, ultramodern sp_complete(); ultramodern::measure_input_latency(); - auto rt64_start = std::chrono::system_clock::now(); + auto rt64_start = std::chrono::high_resolution_clock::now(); RT64SendDL(rdram, &task_action->task); - auto rt64_end = std::chrono::system_clock::now(); + auto rt64_end = std::chrono::high_resolution_clock::now(); dp_complete(); // printf("RT64 ProcessDList time: %d us\n", static_cast(std::chrono::duration_cast(rt64_end - rt64_start).count())); } diff --git a/ultramodern/timer.cpp b/ultramodern/timer.cpp index d71e068..b278ff3 100644 --- a/ultramodern/timer.cpp +++ b/ultramodern/timer.cpp @@ -6,8 +6,13 @@ #include "ultra64.h" #include "ultramodern.hpp" +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include "Windows.h" +#endif + // Start time for the program -static std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now(); +static std::chrono::high_resolution_clock::time_point start_time = std::chrono::high_resolution_clock::now(); // Game speed multiplier (1 means no speedup) constexpr uint32_t speed_multiplier = 1; // N64 CPU counter ticks per millisecond @@ -37,7 +42,7 @@ struct { moodycamel::BlockingConcurrentQueue action_queue{}; } timer_context; -uint64_t duration_to_ticks(std::chrono::system_clock::duration duration) { +uint64_t duration_to_ticks(std::chrono::high_resolution_clock::duration duration) { uint64_t delta_micros = std::chrono::duration_cast(duration).count(); // More accurate than using a floating point timer, will only overflow after running for 12.47 years // Units: (micros * (counts/millis)) / (micros/millis) = counts @@ -51,12 +56,12 @@ std::chrono::microseconds ticks_to_duration(uint64_t ticks) { return ticks * 1000us / counter_per_ms; } -std::chrono::system_clock::time_point ticks_to_timepoint(uint64_t ticks) { +std::chrono::high_resolution_clock::time_point ticks_to_timepoint(uint64_t ticks) { return start_time + ticks_to_duration(ticks); } uint64_t time_now() { - return duration_to_ticks(std::chrono::system_clock::now() - start_time); + return duration_to_ticks(std::chrono::high_resolution_clock::now() - start_time); } void timer_thread(RDRAM_ARG1) { @@ -111,7 +116,7 @@ void timer_thread(RDRAM_ARG1) { active_timers.erase(cur_timer_); // Determine how long to wait to reach the timer's timestamp - auto wait_duration = ticks_to_timepoint(cur_timer->timestamp) - std::chrono::system_clock::now(); + auto wait_duration = ticks_to_timepoint(cur_timer->timestamp) - std::chrono::high_resolution_clock::now(); // Wait for either the duration to complete or a new action to come through if (wait_duration.count() >= 0 && timer_context.action_queue.wait_dequeue_timed(cur_action, wait_duration)) { @@ -142,12 +147,12 @@ uint32_t ultramodern::get_speed_multiplier() { return speed_multiplier; } -std::chrono::system_clock::time_point ultramodern::get_start() { +std::chrono::high_resolution_clock::time_point ultramodern::get_start() { return start_time; } -std::chrono::system_clock::duration ultramodern::time_since_start() { - return std::chrono::system_clock::now() - start_time; +std::chrono::high_resolution_clock::duration ultramodern::time_since_start() { + return std::chrono::high_resolution_clock::now() - start_time; } extern "C" u32 osGetCount() { @@ -188,3 +193,32 @@ extern "C" int osStopTimer(RDRAM_ARG PTR(OSTimer) t_) { // TODO don't blindly return 0 here; requires some response from the timer thread to know what the returned value was return 0; } + +#ifdef _WIN32 + +// The implementations of std::chrono::sleep_until and sleep_for were affected by changing the system clock backwards in older versions +// of Microsoft's STL. This was fixed as of Visual Studio 2022 17.9, but to be safe ultramodern uses Win32 Sleep directly. +void ultramodern::sleep_milliseconds(uint32_t millis) { + Sleep(millis); +} + +void ultramodern::sleep_until(const std::chrono::high_resolution_clock::time_point& time_point) { + auto time_now = std::chrono::high_resolution_clock::now(); + if (time_point > time_now) { + long long delta_ms = std::chrono::ceil(time_point - time_now).count(); + // printf("Sleeping %lld %d ms\n", delta_ms, (uint32_t)delta_ms); + Sleep(delta_ms); + } +} + +#else + +void ultramodern::sleep_milliseconds(uint32_t millis) { + std::this_thread::sleep_for(std::chrono::milliseconds{millis}); +} + +void ultramodern::sleep_until(const std::chrono::high_resolution_clock::time_point& time_point) { + std::this_thread::sleep_until(time_point); +} + +#endif diff --git a/ultramodern/ultramodern.hpp b/ultramodern/ultramodern.hpp index 2ed3367..d436c58 100644 --- a/ultramodern/ultramodern.hpp +++ b/ultramodern/ultramodern.hpp @@ -92,11 +92,13 @@ bool is_game_thread(); void submit_rsp_task(RDRAM_ARG PTR(OSTask) task); void send_si_message(); uint32_t get_speed_multiplier(); -std::chrono::system_clock::time_point get_start(); -std::chrono::system_clock::duration time_since_start(); +std::chrono::high_resolution_clock::time_point get_start(); +std::chrono::high_resolution_clock::duration time_since_start(); void get_window_size(uint32_t& width, uint32_t& height); uint32_t get_target_framerate(uint32_t original); void measure_input_latency(); +void sleep_milliseconds(uint32_t millis); +void sleep_until(const std::chrono::high_resolution_clock::time_point& time_point); // Audio void init_audio();