From 54647bb75081e9c08936679d0531fff94dabf2d5 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 27 Nov 2011 19:50:08 +0000 Subject: [PATCH] Use ::QueryPerformanceCounter() for wxStopWatch implementation in wxMSW. QueryPerformanceCounter() provides higher resolution and precision for measuring time under MSW, even though it suffers from some problems in older Windows versions. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@69834 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- interface/wx/stopwatch.h | 7 ++++ src/common/stopwatch.cpp | 80 +++++++++++++++++++++++++--------------- 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/interface/wx/stopwatch.h b/interface/wx/stopwatch.h index 3f2f98edbe..632c876e0c 100644 --- a/interface/wx/stopwatch.h +++ b/interface/wx/stopwatch.h @@ -25,6 +25,13 @@ wxLogMessage("And calling it twice took $ldms in all", sw.Time()); @endcode + Since wxWidgets 2.9.3 this class uses @c ::QueryPerformanceCounter() + function under MSW to measure the elapsed time. It provides higher + precision than the usual timer functions but can suffer from bugs in its + implementation in some Windows XP versions. If you encounter such problems, + installing a Microsoft hot fix from http://support.microsoft.com/?id=896256 + could be necessary. + @library{wxbase} @category{misc} diff --git a/src/common/stopwatch.cpp b/src/common/stopwatch.cpp index 5ed9058504..922a5f0ed8 100644 --- a/src/common/stopwatch.cpp +++ b/src/common/stopwatch.cpp @@ -95,50 +95,70 @@ #if wxUSE_STOPWATCH +#ifdef __WXMSW__ + +namespace +{ + +struct PerfCounter +{ + PerfCounter() + { + init = false; + } + + bool CanBeUsed() const + { + return freq.QuadPart != 0; + } + + wxCriticalSection cs; + LARGE_INTEGER freq; + bool init; +} gs_perfCounter; + +} // anonymous namespace + +#endif // __WXMSW__ + void wxStopWatch::Start(long t) { -#if 0 -// __WXMSW__ - LARGE_INTEGER frequency_li; - ::QueryPerformanceFrequency( &frequency_li ); - m_frequency = frequency_li.QuadPart; - if (m_frequency == 0) +#ifdef __WXMSW__ + if ( !gs_perfCounter.init ) + { + wxCriticalSectionLocker lock(gs_perfCounter.cs); + ::QueryPerformanceFrequency(&gs_perfCounter.freq); + gs_perfCounter.init = true; + } + + LARGE_INTEGER counter; + if ( gs_perfCounter.CanBeUsed() && ::QueryPerformanceCounter(&counter) ) + { + m_t0 = counter.QuadPart - t*gs_perfCounter.freq.QuadPart/1000; + } + else // Fall back to the generic code below. +#endif // __WXMSW__ { m_t0 = wxGetLocalTimeMillis() - t; } - else - { - LARGE_INTEGER counter_li; - ::QueryPerformanceCounter( &counter_li ); - wxLongLong counter = counter_li.QuadPart; - m_t0 = (counter * 10000 / m_frequency) - t*10; - } -#else - m_t0 = wxGetLocalTimeMillis() - t; -#endif + m_pause = 0; m_pauseCount = 0; } long wxStopWatch::GetElapsedTime() const { -#if 0 -//__WXMSW__ - if (m_frequency == 0) +#ifdef __WXMSW__ + LARGE_INTEGER counter; + if ( gs_perfCounter.CanBeUsed() && ::QueryPerformanceCounter(&counter) ) { - return (wxGetLocalTimeMillis() - m_t0).GetLo(); + wxLongLong delta(counter.QuadPart); + delta -= m_t0; + + return ((delta*1000)/gs_perfCounter.freq.QuadPart).GetLo(); } - else - { - LARGE_INTEGER counter_li; - ::QueryPerformanceCounter( &counter_li ); - wxLongLong counter = counter_li.QuadPart; - wxLongLong res = (counter * 10000 / m_frequency) - m_t0; - return res.GetLo() / 10; - } -#else - return (wxGetLocalTimeMillis() - m_t0).GetLo(); #endif + return (wxGetLocalTimeMillis() - m_t0).GetLo(); } long wxStopWatch::Time() const