diff options
author | Tim Mayberry <mojofunk@gmail.com> | 2015-09-12 21:41:00 +1000 |
---|---|---|
committer | Tim Mayberry <mojofunk@gmail.com> | 2015-09-16 11:22:16 +1000 |
commit | 3f5c01e4eb2d3bd925a7d4d5335cd025546480c6 (patch) | |
tree | 6fc706cc7ba51f1b0f78a42b20d39026f9417e1d /libs/pbd | |
parent | 4ffe8ffc0faef8ea4bb17e27963bf3998a006995 (diff) |
Move Windows timer utility functions from PA backend into libpbd
Diffstat (limited to 'libs/pbd')
-rw-r--r-- | libs/pbd/debug.cc | 1 | ||||
-rw-r--r-- | libs/pbd/pbd/debug.h | 1 | ||||
-rw-r--r-- | libs/pbd/pbd/windows_timer_utils.h | 86 | ||||
-rw-r--r-- | libs/pbd/windows_timer_utils.cc | 184 | ||||
-rw-r--r-- | libs/pbd/wscript | 1 |
5 files changed, 273 insertions, 0 deletions
diff --git a/libs/pbd/debug.cc b/libs/pbd/debug.cc index 3055c396c6..aa55d82f59 100644 --- a/libs/pbd/debug.cc +++ b/libs/pbd/debug.cc @@ -54,6 +54,7 @@ DebugBits PBD::DEBUG::AbstractUI = PBD::new_debug_bit ("abstractui"); DebugBits PBD::DEBUG::FileUtils = PBD::new_debug_bit ("fileutils"); DebugBits PBD::DEBUG::Configuration = PBD::new_debug_bit ("configuration"); DebugBits PBD::DEBUG::UndoHistory = PBD::new_debug_bit ("undohistory"); +DebugBits PBD::DEBUG::Timing = PBD::new_debug_bit ("timing"); /* These are debug bits that are used by backends. Since these are loaded dynamically, after command-line parsing, defining them in code that is part of the backend diff --git a/libs/pbd/pbd/debug.h b/libs/pbd/pbd/debug.h index 065bc4cae0..324638524c 100644 --- a/libs/pbd/pbd/debug.h +++ b/libs/pbd/pbd/debug.h @@ -56,6 +56,7 @@ namespace PBD { LIBPBD_API extern DebugBits Configuration; LIBPBD_API extern DebugBits FileUtils; LIBPBD_API extern DebugBits UndoHistory; + LIBPBD_API extern DebugBits Timing; /* See notes in ../debug.cc on why these are defined here */ diff --git a/libs/pbd/pbd/windows_timer_utils.h b/libs/pbd/pbd/windows_timer_utils.h new file mode 100644 index 0000000000..2e4f800bd5 --- /dev/null +++ b/libs/pbd/pbd/windows_timer_utils.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015 Tim Mayberry <mojofunk@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef PBD_WINDOWS_TIMER_UTILS_H +#define PBD_WINDOWS_TIMER_UTILS_H + +#include <stdint.h> + +namespace PBD { + +namespace MMTIMERS { + +/** + * Set the minimum Multimedia Timer resolution as supported by the system + * @return true if min timer resolution was successfully set + * + * Call reset_resolution to restore old timer resolution + */ +bool set_min_resolution (); + +/** + * Get current Multimedia Timer resolution + * @return true if getting the timer value was successful + */ +bool get_resolution(uint32_t& timer_resolution_us); + +/** + * Set current Multimedia Timer resolution + * @return true if setting the timer value was successful + */ +bool set_resolution(uint32_t timer_resolution_us); + +/** + * Reset the Multimedia Timer back to what it was originally before + * setting the timer resolution. + */ +bool reset_resolution (); + +} // namespace MMTIMERS + +namespace QPC { + +/** + * @return true if QueryPerformanceCounter is usable as a timer source + */ +bool get_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. + */ +int64_t get_microseconds (); + +} // namespace QPC + +/** + * The highest resolution timer source provided by the system. On Vista and + * above this is the value returned by QueryPerformanceCounter(QPC). On XP, + * this will QPC if supported or otherwise g_get_monotonic_time will be used. + * + * @return A timer value in microseconds or -1 in the event that the reading + * the timer source fails. + */ +int64_t get_microseconds (); + +} // namespace PBD + +#endif // PBD_WINDOWS_TIMER_UTILS_H diff --git a/libs/pbd/windows_timer_utils.cc b/libs/pbd/windows_timer_utils.cc new file mode 100644 index 0000000000..fcf8fa8003 --- /dev/null +++ b/libs/pbd/windows_timer_utils.cc @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2015 Tim Mayberry <mojofunk@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "pbd/windows_timer_utils.h" + +#include <windows.h> +#include <mmsystem.h> + +#include "pbd/compose.h" +#include "pbd/debug.h" + +#define DEBUG_TIMING(msg) DEBUG_TRACE (PBD::DEBUG::Timing, msg); + +namespace { + +UINT& +old_timer_resolution () +{ + static UINT timer_res_ms = 0; + return timer_res_ms; +} + +} // anon namespace + +namespace PBD { + +namespace MMTIMERS { + +bool +set_min_resolution () +{ + TIMECAPS caps; + + if (timeGetDevCaps (&caps, sizeof(TIMECAPS)) != TIMERR_NOERROR) { + DEBUG_TIMING ("Could not get timer device capabilities.\n"); + return false; + } + return set_resolution(caps.wPeriodMin); +} + +bool +set_resolution (uint32_t timer_resolution_ms) +{ + TIMECAPS caps; + + if (timeGetDevCaps (&caps, sizeof(TIMECAPS)) != TIMERR_NOERROR) { + DEBUG_TIMING ("Could not get timer device capabilities.\n"); + return false; + } + + UINT old_timer_res = caps.wPeriodMin; + + if (timeBeginPeriod(timer_resolution_ms) != TIMERR_NOERROR) { + DEBUG_TIMING( + string_compose("Could not set minimum timer resolution to %1(ms)\n", + timer_resolution_ms)); + return false; + } + + old_timer_resolution () = old_timer_res; + + DEBUG_TIMING (string_compose ("Multimedia timer resolution set to %1(ms)\n", + caps.wPeriodMin)); + return true; +} + +bool +get_resolution (uint32_t& timer_resolution_ms) +{ + TIMECAPS caps; + + if (timeGetDevCaps(&caps, sizeof(TIMECAPS)) != TIMERR_NOERROR) { + DEBUG_TIMING ("Could not get timer device capabilities.\n"); + return false; + } + timer_resolution_ms = caps.wPeriodMin; + return true; +} + +bool +reset_resolution () +{ + if (old_timer_resolution ()) { + if (timeEndPeriod (old_timer_resolution ()) != TIMERR_NOERROR) { + DEBUG_TIMING ("Could not reset timer resolution.\n"); + return false; + } + } + + DEBUG_TIMING (string_compose ("Multimedia timer resolution set to %1(ms)\n", + old_timer_resolution ())); + + return true; +} + +} // namespace MMTIMERS + +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; +} + +} // anon namespace + +namespace QPC { + +bool +get_timer_valid () +{ + // setup caching the timer frequency + qpc_frequency_cached (); + return qpc_frequency_success (); +} + +int64_t +get_microseconds () +{ + LARGE_INTEGER current_val; + + if (qpc_frequency_success()) { + // 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); + } + } + DEBUG_TIMING ("Could not get QPC timer\n"); + return -1; +} + +} // namespace QPC + +int64_t +get_microseconds () +{ + if (qpc_frequency_success()) { + return QPC::get_microseconds (); + } + // For XP systems that don't support a high-res performance counter + return g_get_monotonic_time (); +} + +} // namespace PBD diff --git a/libs/pbd/wscript b/libs/pbd/wscript index b12210b542..7d74f09786 100644 --- a/libs/pbd/wscript +++ b/libs/pbd/wscript @@ -148,6 +148,7 @@ def build(bld): if bld.env['build_target'] == 'mingw': obj.defines += [ 'NO_POSIX_MEMALIGN' ] obj.source += [ 'windows_special_dirs.cc' ] + obj.source += [ 'windows_timer_utils.cc' ] obj.uselib += ' OLE' if bld.env['BUILD_TESTS'] and bld.is_defined('HAVE_CPPUNIT'): |