Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
qpc.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2010 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * Timer implementation using QueryPerformanceCounter
25  */
26 
27 #include "precompiled.h"
29 
31 
32 #include "lib/sysdep/os_cpu.h"
33 #include "lib/sysdep/os/win/win.h"
34 #include "lib/sysdep/os/win/wutil.h" // wutil_argv
35 #include "lib/sysdep/os/win/whrt/pit.h" // PIT_FREQ
36 #include "lib/sysdep/os/win/whrt/pmt.h" // PMT_FREQ
37 
38 
39 class CounterQPC : public ICounter
40 {
41 public:
43  : m_frequency(-1)
44  {
45  }
46 
47  virtual const wchar_t* Name() const
48  {
49  return L"QPC";
50  }
51 
53  {
54  // note: QPC is observed to be universally supported, but the API
55  // provides for failure, so play it safe.
56 
57  LARGE_INTEGER qpcFreq, qpcValue;
58  const BOOL ok1 = QueryPerformanceFrequency(&qpcFreq);
59  const BOOL ok2 = QueryPerformanceCounter(&qpcValue);
60  if(!ok1 || !ok2)
62  if(!qpcFreq.QuadPart || !qpcValue.QuadPart)
64 
65  m_frequency = (i64)qpcFreq.QuadPart;
66  return INFO::OK;
67  }
68 
69  void Shutdown()
70  {
71  }
72 
73  bool IsSafe() const
74  {
75  // note: we have separate modules that directly access some of the
76  // counters potentially used by QPC. disabling the redundant counters
77  // would be ugly (increased coupling). instead, we'll make sure our
78  // implementations could (if necessary) coexist with QPC, but it
79  // shouldn't come to that since only one counter is needed/used.
80 
81  // the PIT is entirely safe (even if annoyingly slow to read)
82  if(m_frequency == PIT_FREQ)
83  return true;
84 
85  // the PMT is generally safe (see discussion in CounterPmt::IsSafe),
86  // but older QPC implementations had problems with 24-bit rollover.
87  // "System clock problem can inflate benchmark scores"
88  // (http://www.lionbridge.com/bi/cont2000/200012/perfcnt.asp ; no longer
89  // online, nor findable in Google Cache / archive.org) tells of
90  // incorrect values every 4.6 seconds (i.e. 24 bits @ 3.57 MHz) unless
91  // the timer is polled in the meantime. fortunately, this is guaranteed
92  // by our periodic updates (which come at least that often).
93  if(m_frequency == PMT_FREQ)
94  return true;
95 
96  // the TSC has been known to be buggy (even mentioned in MSDN). it is
97  // used on MP HAL systems and can be detected by comparing QPF with the
98  // CPU clock. we consider it unsafe unless the user promises (via
99  // command line) that it's patched and thus reliable on their system.
100  bool usesTsc = IsSimilarMagnitude((double)m_frequency, os_cpu_ClockFrequency());
101  // unconfirmed reports indicate QPC sometimes uses 1/3 of the
102  // CPU clock frequency, so check that as well.
103  usesTsc |= IsSimilarMagnitude((double)m_frequency, os_cpu_ClockFrequency()/3);
104  if(usesTsc)
105  {
106  const bool isTscSafe = wutil_HasCommandLineArgument(L"-wQpcTscSafe");
107  return isTscSafe;
108  }
109 
110  // the HPET is reliable and used on Vista. it can't easily be recognized
111  // since its frequency is variable (the spec says > 10 MHz; the master
112  // 14.318 MHz oscillator is often used). considering frequencies in
113  // [10, 100 MHz) to be a HPET would be dangerous because it may actually
114  // be faster or RDTSC slower. we have to exclude all other cases and
115  // assume it's a HPET - and thus safe - if we get here.
116  return true;
117  }
118 
119  u64 Counter() const
120  {
121  // fairly time-critical here, don't check the return value
122  // (IsSupported made sure it succeeded initially)
123  LARGE_INTEGER qpc_value;
124  (void)QueryPerformanceCounter(&qpc_value);
125  return qpc_value.QuadPart;
126  }
127 
128  size_t CounterBits() const
129  {
130  // there are reports of incorrect rollover handling in the PMT
131  // implementation of QPC (see CounterPMT::IsSafe). however, other
132  // counters would be used on those systems, so it's irrelevant.
133  // we'll report the full 64 bits.
134  return 64;
135  }
136 
137  double NominalFrequency() const
138  {
139  return (double)m_frequency;
140  }
141 
142  double Resolution() const
143  {
144  return 1.0 / m_frequency;
145  }
146 
147 private:
148  // used in several places and QPF is a bit slow+cumbersome.
149  // (i64 allows easier conversion to double)
151 };
152 
153 ICounter* CreateCounterQPC(void* address, size_t size)
154 {
155  ENSURE(sizeof(CounterQPC) <= size);
156  return new(address) CounterQPC();
157 }
static const i64 PMT_FREQ
Definition: pmt.h:30
double os_cpu_ClockFrequency()
Definition: os_cpu.cpp:43
const Status OK
Definition: status.h:386
CounterQPC()
Definition: qpc.cpp:42
virtual const wchar_t * Name() const
Definition: qpc.cpp:47
ICounter * CreateCounterQPC(void *address, size_t size)
Definition: qpc.cpp:153
bool IsSafe() const
Definition: qpc.cpp:73
size_t CounterBits() const
Definition: qpc.cpp:128
int BOOL
Definition: wgl.h:51
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
bool IsSimilarMagnitude(double d1, double d2, const double relativeErrorTolerance=0.05)
Definition: lib.h:105
void Shutdown()
Definition: qpc.cpp:69
i64 Status
Error handling system.
Definition: status.h:171
u64 Counter() const
Definition: qpc.cpp:119
#define u64
Definition: types.h:42
#define i64
Definition: types.h:37
static const i64 PIT_FREQ
Definition: pit.h:39
i64 m_frequency
Definition: qpc.cpp:150
#define WARN_RETURN(status)
Definition: status.h:255
const Status FAIL
Definition: status.h:406
Status Activate()
Definition: qpc.cpp:52
bool wutil_HasCommandLineArgument(const wchar_t *arg)
Definition: wutil.cpp:236
double Resolution() const
actual resolution [s].
Definition: qpc.cpp:142
double NominalFrequency() const
initial measurement of the tick rate.
Definition: qpc.cpp:137