From 119e56e7eb9c58df0ac69e0e94dd6e008701b69c Mon Sep 17 00:00:00 2001 From: Tim Mayberry Date: Wed, 16 Sep 2015 23:21:38 +1000 Subject: [PATCH] Add PBD::QPC::initialize to initialize timer and call it from PBD::init Check timer for invalid frequency Precalculate timer tick rate to save a few instructions Don't use static variables inside functions to avoid checking for initialization Use static functions inside anonymous namespace for internal linkage --- libs/pbd/pbd.cc | 1 + libs/pbd/pbd/windows_timer_utils.h | 17 ++++++-- libs/pbd/windows_timer_utils.cc | 63 +++++++++++++----------------- 3 files changed, 41 insertions(+), 40 deletions(-) diff --git a/libs/pbd/pbd.cc b/libs/pbd/pbd.cc index a4e4f6c9a3..229dc04f32 100644 --- a/libs/pbd/pbd.cc +++ b/libs/pbd/pbd.cc @@ -109,6 +109,7 @@ PBD::init () return false; } + QPC::initialize(); test_timers_from_env (); if (!PBD::MMCSS::initialize()) { diff --git a/libs/pbd/pbd/windows_timer_utils.h b/libs/pbd/pbd/windows_timer_utils.h index 4ebeacd6eb..a8772590d7 100644 --- a/libs/pbd/pbd/windows_timer_utils.h +++ b/libs/pbd/pbd/windows_timer_utils.h @@ -59,20 +59,29 @@ bool reset_resolution(); namespace QPC { +/** + * Initialize the QPC timer, must be called before QPC::get_microseconds will + * return a valid value. + * @return true if QPC timer is usable, use check_timer_valid to try to check + * if it is monotonic. + */ +bool initialize (); + /** * @return true if QueryPerformanceCounter is usable as a timer source * This should always return true for systems > XP as those versions of windows * have there own tests to check timer validity and will select an appropriate - * timer source. + * timer source. This check is not conclusive and there are probably conditions + * under which this check will return true but the timer is not monotonic. */ bool check_timer_valid (); /** * @return the value of the performance counter converted to microseconds * - * If get_counter_valid returns true then get_microseconds will always - * return a positive value. If QPC is not supported(OS < XP) then -1 is - * returned but the MS docs say that this won't occur for systems >= XP. + * If initialize returns true then get_microseconds will always return a + * positive value. If QPC is not supported(OS < XP) then -1 is returned but the + * MS docs say that this won't occur for systems >= XP. */ int64_t get_microseconds (); diff --git a/libs/pbd/windows_timer_utils.cc b/libs/pbd/windows_timer_utils.cc index 2817b8e3a9..ab45e30d80 100644 --- a/libs/pbd/windows_timer_utils.cc +++ b/libs/pbd/windows_timer_utils.cc @@ -23,11 +23,15 @@ #include "pbd/compose.h" #include "pbd/debug.h" +#include "pbd/error.h" + +#include "i18n.h" #define DEBUG_TIMING(msg) DEBUG_TRACE (PBD::DEBUG::Timing, msg); namespace { +static UINT& timer_resolution () { @@ -35,7 +39,7 @@ timer_resolution () return timer_res_ms; } -} +} // namespace namespace PBD { @@ -100,6 +104,7 @@ reset_resolution () DEBUG_TIMING("Could not reset the Timer resolution.\n"); return false; } + DEBUG_TIMING("Reset the Timer resolution.\n"); timer_resolution() = 0; return true; } @@ -108,34 +113,9 @@ reset_resolution () namespace { -bool& -qpc_frequency_success () -{ - static bool success = false; - return success; -} - -LARGE_INTEGER -qpc_frequency () -{ - LARGE_INTEGER freq; - if (QueryPerformanceFrequency(&freq) == 0) { - DEBUG_TIMING ("Failed to determine frequency of QPC\n"); - qpc_frequency_success() = false; - } else { - qpc_frequency_success() = true; - } - - return freq; -} - -LARGE_INTEGER -qpc_frequency_cached () -{ - static LARGE_INTEGER frequency = qpc_frequency (); - return frequency; -} +static double timer_rate_us = 0.0; +static bool test_qpc_validity () { @@ -159,25 +139,37 @@ namespace QPC { bool check_timer_valid () { - // setup caching the timer frequency - qpc_frequency_cached (); - if (!qpc_frequency_success ()) { + if (!timer_rate_us) { return false; } return test_qpc_validity (); } +bool +initialize () +{ + LARGE_INTEGER freq; + if (!QueryPerformanceFrequency(&freq) || freq.QuadPart < 1) { + info << X_("Failed to determine frequency of QPC\n") << endmsg; + timer_rate_us = 0; + } else { + timer_rate_us = 1000000.0 / freq.QuadPart; + } + info << string_compose(X_("QPC timer microseconds per tick: %1\n"), + timer_rate_us) << endmsg; + return !timer_rate_us; +} + int64_t get_microseconds () { LARGE_INTEGER current_val; - if (qpc_frequency_success()) { + if (timer_rate_us) { // MS docs say this will always succeed for systems >= XP but it may // not return a monotonic value with non-invariant TSC's etc if (QueryPerformanceCounter(¤t_val) != 0) { - return (int64_t)(((double)current_val.QuadPart) / - ((double)qpc_frequency_cached().QuadPart) * 1000000.0); + return (int64_t)(current_val.QuadPart * timer_rate_us); } } DEBUG_TIMING ("Could not get QPC timer\n"); @@ -189,8 +181,7 @@ get_microseconds () int64_t get_microseconds () { - qpc_frequency_cached(); - if (qpc_frequency_success()) { + if (timer_rate_us) { return QPC::get_microseconds (); } // For XP systems that don't support a high-res performance counter -- 2.30.2