diff --git a/core/os/os.cpp b/core/os/os.cpp index 753190048094..f1fd329e7ef0 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -47,7 +47,7 @@ OS *OS::get_singleton() { return singleton; } -uint32_t OS::get_ticks_msec() const { +uint32_t OS::get_ticks_msec() { return get_ticks_usec() / 1000; } @@ -93,6 +93,59 @@ uint64_t OS::get_system_time_secs() const { uint64_t OS::get_system_time_msecs() const { return 0; } + +uint64_t OS::get_ticks_usec() { + // This is a workaround for rare OS bugs where a call to get_ticks_raw_usec() + // may return an EARLIER value than that given in a previous call. + // This ensures that the minimum delta will be zero, and prevents bugs which rely on + // the delta being positive. + uint64_t t = get_ticks_raw_usec(); + + // note that get_ticks_raw_usec() may return 0 if the OS API function failed. + // we will deal with this by returning the previous value + if (t == 0) + return _ticks_usec_running_total; + + if (t >= _ticks_usec_prev) + { + // time going forwards, normal case + uint64_t diff = t - _ticks_usec_prev; + + // cap forward time (shouldn't happen except for long stalls, but just in case) + if (diff > 100000) + { + if (diff < 1000000) { + WARN_PRINTS("OS time running forward " + itos(diff) + " usecs detected"); + } + else { + WARN_PRINTS("OS time running forward " + itos(diff / 1000000) + " secs detected"); + } + diff = 100000; + } + + // move our own robust 'fake clock' forward + _ticks_usec_running_total += diff; + } + else + { + // time going backwards, we need to deal with this + // Do not increment running total + uint64_t diff = _ticks_usec_prev - t; + + if (diff < 1000000) { + WARN_PRINTS("OS time running backward " + itos(diff) + " usecs detected"); + } + else { + WARN_PRINTS("OS time running backward " + itos(diff / 1000000) + " secs detected"); + } + } + + // resync each time, the difference should be based on the previous value + _ticks_usec_prev = t; + + return _ticks_usec_running_total; +} + void OS::debug_break(){ // something @@ -769,6 +822,8 @@ OS::OS() { _keep_screen_on = true; // set default value to true, because this had been true before godot 2.0. low_processor_usage_mode = false; low_processor_usage_mode_sleep_usec = 10000; + _ticks_usec_prev = 0; + _ticks_usec_running_total = 0; _verbose_stdout = false; _no_window = false; _exit_code = 0; diff --git a/core/os/os.h b/core/os/os.h index 9b46b43081ee..8b6dd99c6e85 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -60,6 +60,8 @@ class OS { bool _allow_hidpi; bool _allow_layered; bool _use_vsync; + uint64_t _ticks_usec_prev; + uint64_t _ticks_usec_running_total; char *last_error; @@ -138,6 +140,8 @@ class OS { void _ensure_user_data_dir(); virtual bool _check_internal_feature_support(const String &p_feature) = 0; + virtual uint64_t get_ticks_raw_usec() const = 0; + public: typedef int64_t ProcessID; @@ -344,8 +348,8 @@ class OS { virtual uint64_t get_system_time_msecs() const; virtual void delay_usec(uint32_t p_usec) const = 0; - virtual uint64_t get_ticks_usec() const = 0; - uint32_t get_ticks_msec() const; + uint64_t get_ticks_usec(); + uint32_t get_ticks_msec(); uint64_t get_splash_tick_msec() const; virtual bool can_draw() const = 0; diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index ab5590dba415..972c5e36a62a 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -258,17 +258,30 @@ void OS_Unix::delay_usec(uint32_t p_usec) const { while (nanosleep(&rem, &rem) == EINTR) { } } -uint64_t OS_Unix::get_ticks_usec() const { +uint64_t OS_Unix::get_ticks_raw_usec() const { #if defined(__APPLE__) uint64_t longtime = mach_absolute_time() * _clock_scale; #else - // Unchecked return. Static analyzers might complain. - // If _setup_clock() succeeded, we assume clock_gettime() works. struct timespec tv_now = { 0, 0 }; - clock_gettime(GODOT_CLOCK, &tv_now); + + int res = clock_gettime(GODOT_CLOCK, &tv_now); + + // if clock_gettime failed, return zero which is dealt with by wrapper + if (res != 0) + { + WARN_PRINT_ONCE("clock_gettime failed"); + return 0; + } + uint64_t longtime = ((uint64_t)tv_now.tv_nsec / 1000L) + (uint64_t)tv_now.tv_sec * 1000000L; #endif + // _clock_start should never be ahead of longtime + if (_clock_start > longtime) { + WARN_PRINT_ONCE("_clock_start is ahead of clock_gettime"); + return 0; + } + longtime -= _clock_start; return longtime; diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index a263147e23b7..46c159b27391 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -48,6 +48,8 @@ class OS_Unix : public OS { virtual void finalize_core(); + virtual uint64_t get_ticks_raw_usec() const; + String stdin_buf; public: @@ -83,7 +85,6 @@ class OS_Unix : public OS { virtual uint64_t get_system_time_msecs() const; virtual void delay_usec(uint32_t p_usec) const; - virtual uint64_t get_ticks_usec() const; virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL); virtual Error kill(const ProcessID &p_pid); diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index 60f229035573..ba72a16540df 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -620,7 +620,7 @@ void OS_UWP::delay_usec(uint32_t p_usec) const { // no Sleep() WaitForSingleObjectEx(GetCurrentThread(), msec, false); } -uint64_t OS_UWP::get_ticks_usec() const { +uint64_t OS_UWP::get_ticks_raw_usec() const { uint64_t ticks; uint64_t time; diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index 370cab6a9b0c..987c9aa6bc35 100644 --- a/platform/uwp/os_uwp.h +++ b/platform/uwp/os_uwp.h @@ -170,6 +170,8 @@ class OS_UWP : public OS { void process_key_events(); + virtual uint64_t get_ticks_raw_usec() const; + public: // Event to send to the app wrapper HANDLE mouse_mode_changed; @@ -206,7 +208,6 @@ class OS_UWP : public OS { virtual Error set_cwd(const String &p_cwd); virtual void delay_usec(uint32_t p_usec) const; - virtual uint64_t get_ticks_usec() const; virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL); virtual Error kill(const ProcessID &p_pid); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index be325381bb8a..e741354ac3de 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -2284,7 +2284,7 @@ void OS_Windows::delay_usec(uint32_t p_usec) const { else Sleep(p_usec / 1000); } -uint64_t OS_Windows::get_ticks_usec() const { +uint64_t OS_Windows::get_ticks_raw_usec() const { uint64_t ticks; uint64_t time; diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 915d025e3bd0..78c3e32dcc41 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -195,6 +195,8 @@ class OS_Windows : public OS { void process_events(); void process_key_events(); + virtual uint64_t get_ticks_raw_usec() const; + struct ProcessInfo { STARTUPINFO si; @@ -287,7 +289,6 @@ class OS_Windows : public OS { virtual Error set_cwd(const String &p_cwd); virtual void delay_usec(uint32_t p_usec) const; - virtual uint64_t get_ticks_usec() const; virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL); virtual Error kill(const ProcessID &p_pid);