1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkOpenGLRenderTimer.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 
16 #include "vtkOpenGLRenderTimer.h"
17 
18 #include "vtkObjectFactory.h"
19 #include "vtkOpenGLRenderer.h" // For query allocation bug check
20 
21 #include "vtk_glew.h"
22 
23 // glQueryCounter unavailable in OpenGL ES:
24 #ifdef GL_ES_VERSION_3_0
25 #define NO_TIMESTAMP_QUERIES
26 #endif
27 
28 //------------------------------------------------------------------------------
vtkOpenGLRenderTimer()29 vtkOpenGLRenderTimer::vtkOpenGLRenderTimer()
30   : StartReady(false)
31   , EndReady(false)
32   , StartQuery(0)
33   , EndQuery(0)
34   , StartTime(0)
35   , EndTime(0)
36   , ReusableStarted(false)
37   , ReusableEnded(false)
38 {
39 }
40 
41 //------------------------------------------------------------------------------
~vtkOpenGLRenderTimer()42 vtkOpenGLRenderTimer::~vtkOpenGLRenderTimer()
43 {
44   if (this->StartQuery != 0 || this->EndQuery != 0)
45   {
46     this->Reset();
47   }
48 }
49 
50 //------------------------------------------------------------------------------
IsSupported()51 bool vtkOpenGLRenderTimer::IsSupported()
52 {
53 #ifdef NO_TIMESTAMP_QUERIES
54   return false;
55 #else
56   static const bool s = !vtkOpenGLRenderer::HaveAppleQueryAllocationBug();
57   return s;
58 #endif
59 }
60 
61 //------------------------------------------------------------------------------
Reset()62 void vtkOpenGLRenderTimer::Reset()
63 {
64 #ifndef NO_TIMESTAMP_QUERIES
65   if (this->StartQuery == 0 && this->EndQuery == 0)
66   {
67     // short-circuit to avoid checking if queries weren't initialized at all.
68     // this is necessary since `IsSupported` may make OpenGL calls on APPLE
69     // through `HaveAppleQueryAllocationBug` invocation and that may be not be
70     // correct when timers are being destroyed.
71     return;
72   }
73 
74   if (!this->IsSupported())
75   {
76     return;
77   }
78 
79   if (this->StartQuery != 0)
80   {
81     glDeleteQueries(1, static_cast<GLuint*>(&this->StartQuery));
82     this->StartQuery = 0;
83   }
84 
85   if (this->EndQuery != 0)
86   {
87     glDeleteQueries(1, static_cast<GLuint*>(&this->EndQuery));
88     this->EndQuery = 0;
89   }
90 
91   this->StartReady = false;
92   this->EndReady = false;
93   this->StartTime = 0;
94   this->EndTime = 0;
95 #endif // NO_TIMESTAMP_QUERIES
96 }
97 
98 //------------------------------------------------------------------------------
Start()99 void vtkOpenGLRenderTimer::Start()
100 {
101   if (!this->IsSupported())
102   {
103     return;
104   }
105 
106   this->Reset();
107 
108 #ifndef NO_TIMESTAMP_QUERIES
109   glGenQueries(1, static_cast<GLuint*>(&this->StartQuery));
110   glQueryCounter(static_cast<GLuint>(this->StartQuery), GL_TIMESTAMP);
111 #endif // NO_TIMESTAMP_QUERIES
112 }
113 
114 //------------------------------------------------------------------------------
Stop()115 void vtkOpenGLRenderTimer::Stop()
116 {
117 #ifndef NO_TIMESTAMP_QUERIES
118   if (!this->IsSupported())
119   {
120     return;
121   }
122 
123   if (this->EndQuery != 0)
124   {
125     vtkGenericWarningMacro("vtkOpenGLRenderTimer::Stop called before "
126                            "resetting. Ignoring.");
127     return;
128   }
129 
130   if (this->StartQuery == 0)
131   {
132     vtkGenericWarningMacro("vtkOpenGLRenderTimer::Stop called before "
133                            "vtkOpenGLRenderTimer::Start. Ignoring.");
134     return;
135   }
136 
137   glGenQueries(1, static_cast<GLuint*>(&this->EndQuery));
138   glQueryCounter(static_cast<GLuint>(this->EndQuery), GL_TIMESTAMP);
139 #endif // NO_TIMESTAMP_QUERIES
140 }
141 
142 //------------------------------------------------------------------------------
Started()143 bool vtkOpenGLRenderTimer::Started()
144 {
145 #ifndef NO_TIMESTAMP_QUERIES
146   return this->StartQuery != 0;
147 #else  // NO_TIMESTAMP_QUERIES
148   return false;
149 #endif // NO_TIMESTAMP_QUERIES
150 }
151 
152 //------------------------------------------------------------------------------
Stopped()153 bool vtkOpenGLRenderTimer::Stopped()
154 {
155 #ifndef NO_TIMESTAMP_QUERIES
156   return this->EndQuery != 0;
157 #else  // NO_TIMESTAMP_QUERIES
158   return false;
159 #endif // NO_TIMESTAMP_QUERIES
160 }
161 
162 //------------------------------------------------------------------------------
Ready()163 bool vtkOpenGLRenderTimer::Ready()
164 {
165 #ifndef NO_TIMESTAMP_QUERIES
166   if (!this->IsSupported())
167   {
168     return false;
169   }
170 
171   if (!this->StartReady)
172   {
173     GLint ready;
174     glGetQueryObjectiv(static_cast<GLuint>(this->StartQuery), GL_QUERY_RESULT_AVAILABLE, &ready);
175     if (!ready)
176     {
177       return false;
178     }
179 
180     this->StartReady = true;
181     glGetQueryObjectui64v(static_cast<GLuint>(this->StartQuery), GL_QUERY_RESULT,
182       reinterpret_cast<GLuint64*>(&this->StartTime));
183   }
184 
185   if (!this->EndReady)
186   {
187     GLint ready;
188     glGetQueryObjectiv(static_cast<GLuint>(this->EndQuery), GL_QUERY_RESULT_AVAILABLE, &ready);
189     if (!ready)
190     {
191       return false;
192     }
193 
194     this->EndReady = true;
195     glGetQueryObjectui64v(static_cast<GLuint>(this->EndQuery), GL_QUERY_RESULT,
196       reinterpret_cast<GLuint64*>(&this->EndTime));
197   }
198 #endif // NO_TIMESTAMP_QUERIES
199 
200   return true;
201 }
202 
203 //------------------------------------------------------------------------------
GetElapsedSeconds()204 float vtkOpenGLRenderTimer::GetElapsedSeconds()
205 {
206 #ifndef NO_TIMESTAMP_QUERIES
207   if (!this->Ready())
208   {
209     return 0.f;
210   }
211 
212   return (this->EndTime - this->StartTime) * 1e-9f;
213 #else  // NO_TIMESTAMP_QUERIES
214   return 0.f;
215 #endif // NO_TIMESTAMP_QUERIES
216 }
217 
218 //------------------------------------------------------------------------------
GetElapsedMilliseconds()219 float vtkOpenGLRenderTimer::GetElapsedMilliseconds()
220 {
221 #ifndef NO_TIMESTAMP_QUERIES
222   if (!this->Ready())
223   {
224     return 0.f;
225   }
226 
227   return (this->EndTime - this->StartTime) * 1e-6f;
228 #else  // NO_TIMESTAMP_QUERIES
229   return 0.f;
230 #endif // NO_TIMESTAMP_QUERIES
231 }
232 
233 //------------------------------------------------------------------------------
GetElapsedNanoseconds()234 vtkTypeUInt64 vtkOpenGLRenderTimer::GetElapsedNanoseconds()
235 {
236 #ifndef NO_TIMESTAMP_QUERIES
237   if (!this->Ready())
238   {
239     return 0;
240   }
241 
242   return (this->EndTime - this->StartTime);
243 #else  // NO_TIMESTAMP_QUERIES
244   return 0;
245 #endif // NO_TIMESTAMP_QUERIES
246 }
247 
248 //------------------------------------------------------------------------------
GetStartTime()249 vtkTypeUInt64 vtkOpenGLRenderTimer::GetStartTime()
250 {
251 #ifndef NO_TIMESTAMP_QUERIES
252   if (!this->Ready())
253   {
254     return 0;
255   }
256 
257   return this->StartTime;
258 #else  // NO_TIMESTAMP_QUERIES
259   return 0;
260 #endif // NO_TIMESTAMP_QUERIES
261 }
262 
263 //------------------------------------------------------------------------------
GetStopTime()264 vtkTypeUInt64 vtkOpenGLRenderTimer::GetStopTime()
265 {
266 #ifndef NO_TIMESTAMP_QUERIES
267   if (!this->Ready())
268   {
269     return 0;
270   }
271 
272   return this->EndTime;
273 #else  // NO_TIMESTAMP_QUERIES
274   return 0;
275 #endif // NO_TIMESTAMP_QUERIES
276 }
277 
278 //------------------------------------------------------------------------------
ReleaseGraphicsResources()279 void vtkOpenGLRenderTimer::ReleaseGraphicsResources()
280 {
281   this->Reset();
282 }
283 
284 //------------------------------------------------------------------------------
ReusableStart()285 void vtkOpenGLRenderTimer::ReusableStart()
286 {
287 #ifndef NO_TIMESTAMP_QUERIES
288   if (!this->IsSupported())
289   {
290     return;
291   }
292 
293   if (this->StartQuery == 0)
294   {
295     glGenQueries(1, static_cast<GLuint*>(&this->StartQuery));
296     glQueryCounter(static_cast<GLuint>(this->StartQuery), GL_TIMESTAMP);
297     this->ReusableStarted = true;
298     this->ReusableEnded = false;
299   }
300   if (!this->ReusableStarted)
301   {
302     glQueryCounter(static_cast<GLuint>(this->StartQuery), GL_TIMESTAMP);
303     this->ReusableStarted = true;
304     this->ReusableEnded = false;
305   }
306 #endif // NO_TIMESTAMP_QUERIES
307 }
308 
309 //------------------------------------------------------------------------------
ReusableStop()310 void vtkOpenGLRenderTimer::ReusableStop()
311 {
312 #ifndef NO_TIMESTAMP_QUERIES
313   if (!this->IsSupported())
314   {
315     return;
316   }
317 
318   if (!this->ReusableStarted)
319   {
320     vtkGenericWarningMacro("vtkOpenGLRenderTimer::ReusableStop called before "
321                            "vtkOpenGLRenderTimer::ReusableStart. Ignoring.");
322     return;
323   }
324 
325   if (this->EndQuery == 0)
326   {
327     glGenQueries(1, static_cast<GLuint*>(&this->EndQuery));
328     glQueryCounter(static_cast<GLuint>(this->EndQuery), GL_TIMESTAMP);
329     this->ReusableEnded = true;
330   }
331   if (!this->ReusableEnded)
332   {
333     glQueryCounter(static_cast<GLuint>(this->EndQuery), GL_TIMESTAMP);
334     this->ReusableEnded = true;
335   }
336 #endif // NO_TIMESTAMP_QUERIES
337 }
338 
339 //------------------------------------------------------------------------------
GetReusableElapsedSeconds()340 float vtkOpenGLRenderTimer::GetReusableElapsedSeconds()
341 {
342 #ifndef NO_TIMESTAMP_QUERIES
343   // we do not have an end query yet so we cannot have a time
344   if (!this->EndQuery)
345   {
346     return 0.0;
347   }
348 
349   if (this->ReusableStarted && !this->StartReady)
350   {
351     GLint ready;
352     glGetQueryObjectiv(static_cast<GLuint>(this->StartQuery), GL_QUERY_RESULT_AVAILABLE, &ready);
353     if (ready)
354     {
355       this->StartReady = true;
356     }
357   }
358 
359   if (this->StartReady && this->ReusableEnded && !this->EndReady)
360   {
361     GLint ready;
362     glGetQueryObjectiv(static_cast<GLuint>(this->EndQuery), GL_QUERY_RESULT_AVAILABLE, &ready);
363     if (ready)
364     {
365       this->EndReady = true;
366     }
367   }
368 
369   // if everything is ready read the times to get a new elapsed time
370   // and then prep for a new flight. This also has the benefit that
371   // if no one is getting the elapsed time then nothing is done
372   // beyond the first flight.
373   if (this->StartReady && this->EndReady)
374   {
375     glGetQueryObjectui64v(static_cast<GLuint>(this->StartQuery), GL_QUERY_RESULT,
376       reinterpret_cast<GLuint64*>(&this->StartTime));
377     glGetQueryObjectui64v(static_cast<GLuint>(this->EndQuery), GL_QUERY_RESULT,
378       reinterpret_cast<GLuint64*>(&this->EndTime));
379     // it was ready so prepare another flight
380     this->ReusableStarted = false;
381     this->ReusableEnded = false;
382     this->StartReady = false;
383     this->EndReady = false;
384   }
385 
386   return (this->EndTime - this->StartTime) * 1e-9f;
387 #else  // NO_TIMESTAMP_QUERIES
388   return 0.f;
389 #endif // NO_TIMESTAMP_QUERIES
390 }
391