diff --git a/CMakeLists.txt b/CMakeLists.txt index 89e5ea64d..35bd1168f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,11 @@ endif() # Android options if(SFML_OS_ANDROID) + sfml_set_option(SFML_ANDROID_USE_SUSPEND_AWARE_CLOCK FALSE BOOL "TRUE to use an sf::Clock implementation which takes system sleep time into account (keeps advancing during suspension), FALSE to default to another available monotonic clock") + if (SFML_ANDROID_USE_SUSPEND_AWARE_CLOCK) + add_definitions(-DSFML_ANDROID_USE_SUSPEND_AWARE_CLOCK) + endif() + # avoid missing libraries when building SFML for Android with NDK r19c and later set(CMAKE_FIND_ROOT_PATH "${PROJECT_SOURCE_DIR};${CMAKE_FIND_ROOT_PATH}") diff --git a/include/SFML/System/Clock.hpp b/include/SFML/System/Clock.hpp index d3239adcf..3ae671177 100644 --- a/include/SFML/System/Clock.hpp +++ b/include/SFML/System/Clock.hpp @@ -30,10 +30,56 @@ //////////////////////////////////////////////////////////// #include #include +#include +#include +#include + +#ifdef SFML_SYSTEM_ANDROID +#include +#endif namespace sf { +namespace priv +{ + +//////////////////////////////////////////////////////////// +/// \brief Chooses a monotonic clock of highest resolution +/// +/// The high_resolution_clock is usually an alias for other +/// clocks: steady_clock or system_clock, whichever has a +/// higher precision. +/// +/// sf::Clock, however, is aimed towards monotonic time +/// measurements and so system_clock could never be a choice +/// as its subject to discontinuous jumps in the system time +/// (e.g., if the system administrator manually changes +/// the clock), and by the incremental adjustments performed +/// by `adjtime` and Network Time Protocol. On the other +/// hand, monotonic clocks are unaffected by this behavior. +/// +/// Note: Linux implementation of a monotonic clock that +/// takes sleep time into account is represented by +/// CLOCK_BOOTTIME. Android devices can define the macro: +/// SFML_ANDROID_USE_SUSPEND_AWARE_CLOCK to use a separate +/// implementation of that clock, instead. +/// +/// For more information on Linux clocks visit: +/// https://linux.die.net/man/2/clock_gettime +/// +//////////////////////////////////////////////////////////// +#if defined(SFML_SYSTEM_ANDROID) && defined(SFML_ANDROID_USE_SUSPEND_AWARE_CLOCK) + using MostSuitableClock = SuspendAwareClock; +#else + using MostSuitableClock = std::conditional_t< + std::chrono::high_resolution_clock::is_steady, + std::chrono::high_resolution_clock, + std::chrono::steady_clock>; +#endif + +} // namespace priv + //////////////////////////////////////////////////////////// /// \brief Utility class that measures the elapsed time /// @@ -74,11 +120,28 @@ public: Time restart(); private: + using ClockImpl = priv::MostSuitableClock; + + static_assert(ClockImpl::is_steady, + "Provided implementation is not a monotonic clock"); + static_assert(std::ratio_less_equal::value, + "Clock resolution is too low. Expecting at least a microsecond precision"); + + //////////////////////////////////////////////////////////// + /// \brief Convert clock duration to Time + /// + /// This function acts as a utility for converting clock + /// duration type instance into sf::Time + /// + /// \return Time instance + /// + //////////////////////////////////////////////////////////// + static Time durationToTime(ClockImpl::duration duration); //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - Time m_startTime; //!< Time of last reset, in microseconds + ClockImpl::time_point m_startTime; //!< Time of last reset }; } // namespace sf diff --git a/src/SFML/System/Win32/ClockImpl.hpp b/include/SFML/System/SuspendAwareClock.hpp similarity index 52% rename from src/SFML/System/Win32/ClockImpl.hpp rename to include/SFML/System/SuspendAwareClock.hpp index 5a95da25c..2328ee582 100644 --- a/src/SFML/System/Win32/ClockImpl.hpp +++ b/include/SFML/System/SuspendAwareClock.hpp @@ -22,40 +22,59 @@ // //////////////////////////////////////////////////////////// -#ifndef SFML_CLOCKIMPLWIN32_HPP -#define SFML_CLOCKIMPLWIN32_HPP + +#ifndef SFML_SUSPENDAWARECLOCK_HPP +#define SFML_SUSPENDAWARECLOCK_HPP //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include -#include +#include +#include +#include namespace sf { -namespace priv -{ //////////////////////////////////////////////////////////// -/// \brief Windows implementation of sf::Clock +/// \brief Android, chrono-compatible, suspend-aware clock +/// +/// Linux steady clock is represented by CLOCK_MONOTONIC. +/// However, this implementation does not work properly for +/// long-running clocks that work in the background when the +/// system is suspended. +/// +/// SuspendAwareClock uses CLOCK_BOOTTIME which is identical +/// to CLOCK_MONOTONIC, except that it also includes any time +/// that the system is suspended. +/// +/// Note: In most cases, CLOCK_MONOTONIC is a better choice. +/// Make sure this implementation is required for your use case. /// //////////////////////////////////////////////////////////// -class ClockImpl +class SFML_SYSTEM_API SuspendAwareClock { public: + //////////////////////////////////////////////////////////// + /// \brief Type traits and static members + /// + /// These type traits and static members meet the requirements + /// of a Clock concept in the C++ Standard. More specifically, + /// TrivialClock requirements are met. Thus, naming convention + /// has been kept consistent to allow for extended use e.g. + /// https://en.cppreference.com/w/cpp/chrono/is_clock + /// + //////////////////////////////////////////////////////////// + using duration = std::chrono::nanoseconds; + using rep = duration::rep; + using period = duration::period; + using time_point = std::chrono::time_point; - //////////////////////////////////////////////////////////// - /// \brief Get the current time - /// - /// \return Current time - /// - //////////////////////////////////////////////////////////// - static Time getCurrentTime(); + static constexpr bool is_steady = true; + + static time_point now() noexcept; }; -} // namespace priv - } // namespace sf - -#endif // SFML_CLOCKIMPLWIN32_HPP +#endif // SFML_SUSPENDAWARECLOCK_HPP diff --git a/src/SFML/System/Unix/SleepImpl.hpp b/src/SFML/System/Android/SuspendAwareClock.cpp similarity index 75% rename from src/SFML/System/Unix/SleepImpl.hpp rename to src/SFML/System/Android/SuspendAwareClock.cpp index c63953dcc..ac401aff2 100644 --- a/src/SFML/System/Unix/SleepImpl.hpp +++ b/src/SFML/System/Android/SuspendAwareClock.cpp @@ -22,31 +22,24 @@ // //////////////////////////////////////////////////////////// -#ifndef SFML_SLEEPIMPLUNIX_HPP -#define SFML_SLEEPIMPLUNIX_HPP - //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include -#include - +#include +#include namespace sf { -namespace priv -{ -//////////////////////////////////////////////////////////// -/// \brief Unix implementation of sf::Sleep -/// -/// \param time Time to sleep -/// -//////////////////////////////////////////////////////////// -void sleepImpl(Time time); -} // namespace priv +SuspendAwareClock::time_point SuspendAwareClock::now() noexcept +{ + ::timespec ts; +#ifdef CLOCK_BOOTTIME + clock_gettime(CLOCK_BOOTTIME, &ts); +#else + #error "CLOCK_BOOTTIME is essential for SuspendAwareClock to work" +#endif // CLOCK_BOOTTIME + return time_point(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)); +} } // namespace sf - - -#endif // SFML_SLEEPIMPLUNIX_HPP diff --git a/src/SFML/System/CMakeLists.txt b/src/SFML/System/CMakeLists.txt index de3aa0b2c..4c108936a 100644 --- a/src/SFML/System/CMakeLists.txt +++ b/src/SFML/System/CMakeLists.txt @@ -29,38 +29,22 @@ set(SRC ${INCROOT}/FileInputStream.hpp ${SRCROOT}/MemoryInputStream.cpp ${INCROOT}/MemoryInputStream.hpp + ${INCROOT}/SuspendAwareClock.hpp ) source_group("" FILES ${SRC}) # add platform specific sources -if(SFML_OS_WINDOWS) - set(PLATFORM_SRC - ${SRCROOT}/Win32/ClockImpl.cpp - ${SRCROOT}/Win32/ClockImpl.hpp - ${SRCROOT}/Win32/SleepImpl.cpp - ${SRCROOT}/Win32/SleepImpl.hpp +if(SFML_OS_ANDROID) + set(PLATFORM_SRC ${PLATFORM_SRC} + ${SRCROOT}/Android/Activity.hpp + ${SRCROOT}/Android/Activity.cpp + ${SRCROOT}/Android/NativeActivity.cpp + ${SRCROOT}/Android/ResourceStream.cpp + ${SRCROOT}/Android/ResourceStream.cpp + ${SRCROOT}/Android/SuspendAwareClock.cpp ) - source_group("windows" FILES ${PLATFORM_SRC}) -else() - set(PLATFORM_SRC - ${SRCROOT}/Unix/ClockImpl.cpp - ${SRCROOT}/Unix/ClockImpl.hpp - ${SRCROOT}/Unix/SleepImpl.cpp - ${SRCROOT}/Unix/SleepImpl.hpp - ) - - if(SFML_OS_ANDROID) - set(PLATFORM_SRC ${PLATFORM_SRC} - ${SRCROOT}/Android/Activity.hpp - ${SRCROOT}/Android/Activity.cpp - ${SRCROOT}/Android/NativeActivity.cpp - ${SRCROOT}/Android/ResourceStream.cpp - ${SRCROOT}/Android/ResourceStream.cpp - ) - endif() - - source_group("unix" FILES ${PLATFORM_SRC}) endif() +source_group("unix" FILES ${PLATFORM_SRC}) # define the sfml-system target sfml_add_library(sfml-system diff --git a/src/SFML/System/Clock.cpp b/src/SFML/System/Clock.cpp index 5e3ec4747..35c531f96 100644 --- a/src/SFML/System/Clock.cpp +++ b/src/SFML/System/Clock.cpp @@ -27,18 +27,12 @@ //////////////////////////////////////////////////////////// #include -#if defined(SFML_SYSTEM_WINDOWS) - #include -#else - #include -#endif - namespace sf { //////////////////////////////////////////////////////////// Clock::Clock() : -m_startTime(priv::ClockImpl::getCurrentTime()) +m_startTime(ClockImpl::now()) { } @@ -46,18 +40,27 @@ m_startTime(priv::ClockImpl::getCurrentTime()) //////////////////////////////////////////////////////////// Time Clock::getElapsedTime() const { - return priv::ClockImpl::getCurrentTime() - m_startTime; + return durationToTime(ClockImpl::now() - m_startTime); } //////////////////////////////////////////////////////////// Time Clock::restart() { - Time now = priv::ClockImpl::getCurrentTime(); - Time elapsed = now - m_startTime; + const ClockImpl::time_point now = ClockImpl::now(); + Time elapsed = durationToTime(now - m_startTime); m_startTime = now; return elapsed; } + +//////////////////////////////////////////////////////////// +Time Clock::durationToTime(Clock::ClockImpl::duration duration) +{ + using std::chrono::duration_cast; + using std::chrono::microseconds; + return sf::microseconds(duration_cast(duration).count()); +} + } // namespace sf diff --git a/src/SFML/System/Sleep.cpp b/src/SFML/System/Sleep.cpp index 639df93bf..0e03da8e5 100644 --- a/src/SFML/System/Sleep.cpp +++ b/src/SFML/System/Sleep.cpp @@ -26,12 +26,8 @@ // Headers //////////////////////////////////////////////////////////// #include - -#if defined(SFML_SYSTEM_WINDOWS) - #include -#else - #include -#endif +#include +#include namespace sf @@ -39,8 +35,8 @@ namespace sf //////////////////////////////////////////////////////////// void sleep(Time duration) { - if (duration >= Time::Zero) - priv::sleepImpl(duration); + const auto time = std::chrono::duration(duration.asMicroseconds()); + std::this_thread::sleep_for(time); } } // namespace sf diff --git a/src/SFML/System/Unix/ClockImpl.cpp b/src/SFML/System/Unix/ClockImpl.cpp deleted file mode 100644 index 85cf18a07..000000000 --- a/src/SFML/System/Unix/ClockImpl.cpp +++ /dev/null @@ -1,64 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2021 Laurent Gomila (laurent@sfml-dev.org) -// -// This software is provided 'as-is', without any express or implied warranty. -// In no event will the authors be held liable for any damages arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it freely, -// subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; -// you must not claim that you wrote the original software. -// If you use this software in a product, an acknowledgment -// in the product documentation would be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, -// and must not be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// -//////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#if defined(SFML_SYSTEM_MACOS) || defined(SFML_SYSTEM_IOS) - #include -#else - #include -#endif - - -namespace sf -{ -namespace priv -{ -//////////////////////////////////////////////////////////// -Time ClockImpl::getCurrentTime() -{ -#if defined(SFML_SYSTEM_MACOS) || defined(SFML_SYSTEM_IOS) - - // Mac OS X specific implementation (it doesn't support clock_gettime) - static mach_timebase_info_data_t frequency = {0, 0}; - if (frequency.denom == 0) - mach_timebase_info(&frequency); - Uint64 nanoseconds = mach_absolute_time() * frequency.numer / frequency.denom; - return sf::microseconds(nanoseconds / 1000); - -#else - - // POSIX implementation - timespec time; - clock_gettime(CLOCK_MONOTONIC, &time); - return sf::microseconds(time.tv_sec * 1000000 + time.tv_nsec / 1000); - -#endif -} - -} // namespace priv - -} // namespace sf diff --git a/src/SFML/System/Unix/ClockImpl.hpp b/src/SFML/System/Unix/ClockImpl.hpp deleted file mode 100644 index 21b011822..000000000 --- a/src/SFML/System/Unix/ClockImpl.hpp +++ /dev/null @@ -1,61 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2021 Laurent Gomila (laurent@sfml-dev.org) -// -// This software is provided 'as-is', without any express or implied warranty. -// In no event will the authors be held liable for any damages arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it freely, -// subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; -// you must not claim that you wrote the original software. -// If you use this software in a product, an acknowledgment -// in the product documentation would be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, -// and must not be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// -//////////////////////////////////////////////////////////// - -#ifndef SFML_CLOCKIMPLUNIX_HPP -#define SFML_CLOCKIMPLUNIX_HPP - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include - - -namespace sf -{ -namespace priv -{ -//////////////////////////////////////////////////////////// -/// \brief Unix implementation of sf::Clock -/// -//////////////////////////////////////////////////////////// -class ClockImpl -{ -public: - - //////////////////////////////////////////////////////////// - /// \brief Get the current time - /// - /// \return Current time - /// - //////////////////////////////////////////////////////////// - static Time getCurrentTime(); -}; - -} // namespace priv - -} // namespace sf - - -#endif // SFML_CLOCKIMPLUNIX_HPP diff --git a/src/SFML/System/Unix/SleepImpl.cpp b/src/SFML/System/Unix/SleepImpl.cpp deleted file mode 100644 index 670702fab..000000000 --- a/src/SFML/System/Unix/SleepImpl.cpp +++ /dev/null @@ -1,59 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2021 Laurent Gomila (laurent@sfml-dev.org) -// -// This software is provided 'as-is', without any express or implied warranty. -// In no event will the authors be held liable for any damages arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it freely, -// subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; -// you must not claim that you wrote the original software. -// If you use this software in a product, an acknowledgment -// in the product documentation would be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, -// and must not be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// -//////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include - - -namespace sf -{ -namespace priv -{ -//////////////////////////////////////////////////////////// -void sleepImpl(Time time) -{ - Int64 usecs = time.asMicroseconds(); - - // Construct the time to wait - timespec ti; - ti.tv_nsec = static_cast((usecs % 1000000) * 1000); - ti.tv_sec = static_cast(usecs / 1000000); - - // Wait... - // If nanosleep returns -1, we check errno. If it is EINTR - // nanosleep was interrupted and has set ti to the remaining - // duration. We continue sleeping until the complete duration - // has passed. We stop sleeping if it was due to an error. - while ((nanosleep(&ti, &ti) == -1) && (errno == EINTR)) - { - } -} - -} // namespace priv - -} // namespace sf diff --git a/src/SFML/System/Win32/ClockImpl.cpp b/src/SFML/System/Win32/ClockImpl.cpp deleted file mode 100644 index af136badf..000000000 --- a/src/SFML/System/Win32/ClockImpl.cpp +++ /dev/null @@ -1,87 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2021 Laurent Gomila (laurent@sfml-dev.org) -// -// This software is provided 'as-is', without any express or implied warranty. -// In no event will the authors be held liable for any damages arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it freely, -// subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; -// you must not claim that you wrote the original software. -// If you use this software in a product, an acknowledgment -// in the product documentation would be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, -// and must not be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// -//////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include -#include - - -namespace -{ - LARGE_INTEGER getFrequency() - { - LARGE_INTEGER frequency; - QueryPerformanceFrequency(&frequency); - return frequency; - } - - bool isWindowsXpOrOlder() - { - // Windows XP was the last 5.x version of Windows - return static_cast(LOBYTE(LOWORD(GetVersion()))) < 6; - } -} - -namespace sf -{ -namespace priv -{ -//////////////////////////////////////////////////////////// -Time ClockImpl::getCurrentTime() -{ - // Calculate inverse of frequency multiplied by 1000000 to prevent overflow in final calculation - // Frequency is constant across the program lifetime - static double inverse = 1000000.0 / static_cast(getFrequency().QuadPart); - - // Detect if we are on Windows XP or older - static bool oldWindows = isWindowsXpOrOlder(); - - LARGE_INTEGER time; - - if (oldWindows) - { - static std::recursive_mutex oldWindowsMutex; - - // Acquire a lock (CRITICAL_SECTION) to prevent travelling back in time - std::scoped_lock lock(oldWindowsMutex); - - // Get the current time - QueryPerformanceCounter(&time); - } - else - { - // Get the current time - QueryPerformanceCounter(&time); - } - - // Return the current time as microseconds - return sf::microseconds(static_cast(static_cast(time.QuadPart) * inverse)); -} - -} // namespace priv - -} // namespace sf diff --git a/src/SFML/System/Win32/SleepImpl.cpp b/src/SFML/System/Win32/SleepImpl.cpp deleted file mode 100644 index 056f1c5f3..000000000 --- a/src/SFML/System/Win32/SleepImpl.cpp +++ /dev/null @@ -1,55 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2021 Laurent Gomila (laurent@sfml-dev.org) -// -// This software is provided 'as-is', without any express or implied warranty. -// In no event will the authors be held liable for any damages arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it freely, -// subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; -// you must not claim that you wrote the original software. -// If you use this software in a product, an acknowledgment -// in the product documentation would be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, -// and must not be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// -//////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include - - -namespace sf -{ -namespace priv -{ -//////////////////////////////////////////////////////////// -void sleepImpl(Time time) -{ - // Get the supported timer resolutions on this system - TIMECAPS tc; - timeGetDevCaps(&tc, sizeof(TIMECAPS)); - - // Set the timer resolution to the minimum for the Sleep call - timeBeginPeriod(tc.wPeriodMin); - - // Wait... - ::Sleep(static_cast(time.asMilliseconds())); - - // Reset the timer resolution back to the system default - timeEndPeriod(tc.wPeriodMin); -} - -} // namespace priv - -} // namespace sf diff --git a/src/SFML/System/Win32/SleepImpl.hpp b/src/SFML/System/Win32/SleepImpl.hpp deleted file mode 100644 index f7073eeeb..000000000 --- a/src/SFML/System/Win32/SleepImpl.hpp +++ /dev/null @@ -1,52 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// SFML - Simple and Fast Multimedia Library -// Copyright (C) 2007-2021 Laurent Gomila (laurent@sfml-dev.org) -// -// This software is provided 'as-is', without any express or implied warranty. -// In no event will the authors be held liable for any damages arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it freely, -// subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; -// you must not claim that you wrote the original software. -// If you use this software in a product, an acknowledgment -// in the product documentation would be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, -// and must not be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// -//////////////////////////////////////////////////////////// - -#ifndef SFML_SLEEPIMPLWIN32_HPP -#define SFML_SLEEPIMPLWIN32_HPP - -//////////////////////////////////////////////////////////// -// Headers -//////////////////////////////////////////////////////////// -#include -#include - - -namespace sf -{ -namespace priv -{ -//////////////////////////////////////////////////////////// -/// \brief Windows implementation of sf::Sleep -/// -/// \param time Time to sleep -/// -//////////////////////////////////////////////////////////// -void sleepImpl(Time time); - -} // namespace priv - -} // namespace sf - - -#endif // SFML_SLEEPIMPLWIN32_HPP