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 #if defined(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->IsSupported())
66   {
67     return;
68   }
69 
70   if (this->StartQuery != 0)
71   {
72     glDeleteQueries(1, static_cast<GLuint*>(&this->StartQuery));
73     this->StartQuery = 0;
74   }
75 
76   if (this->EndQuery != 0)
77   {
78     glDeleteQueries(1, static_cast<GLuint*>(&this->EndQuery));
79     this->EndQuery = 0;
80   }
81 
82   this->StartReady = false;
83   this->EndReady = false;
84   this->StartTime = 0;
85   this->EndTime = 0;
86 #endif // NO_TIMESTAMP_QUERIES
87 }
88 
89 //------------------------------------------------------------------------------
Start()90 void vtkOpenGLRenderTimer::Start()
91 {
92   if (!this->IsSupported())
93   {
94     return;
95   }
96 
97   this->Reset();
98 
99 #ifndef NO_TIMESTAMP_QUERIES
100   glGenQueries(1, static_cast<GLuint*>(&this->StartQuery));
101   glQueryCounter(static_cast<GLuint>(this->StartQuery), GL_TIMESTAMP);
102 #endif // NO_TIMESTAMP_QUERIES
103 }
104 
105 //------------------------------------------------------------------------------
Stop()106 void vtkOpenGLRenderTimer::Stop()
107 {
108 #ifndef NO_TIMESTAMP_QUERIES
109   if (!this->IsSupported())
110   {
111     return;
112   }
113 
114   if (this->EndQuery != 0)
115   {
116     vtkGenericWarningMacro("vtkOpenGLRenderTimer::Stop called before "
117                            "resetting. Ignoring.");
118     return;
119   }
120 
121   if (this->StartQuery == 0)
122   {
123     vtkGenericWarningMacro("vtkOpenGLRenderTimer::Stop called before "
124                            "vtkOpenGLRenderTimer::Start. Ignoring.");
125     return;
126   }
127 
128   glGenQueries(1, static_cast<GLuint*>(&this->EndQuery));
129   glQueryCounter(static_cast<GLuint>(this->EndQuery), GL_TIMESTAMP);
130 #endif // NO_TIMESTAMP_QUERIES
131 }
132 
133 //------------------------------------------------------------------------------
Started()134 bool vtkOpenGLRenderTimer::Started()
135 {
136 #ifndef NO_TIMESTAMP_QUERIES
137   return this->StartQuery != 0;
138 #else // NO_TIMESTAMP_QUERIES
139   return false;
140 #endif // NO_TIMESTAMP_QUERIES
141 }
142 
143 //------------------------------------------------------------------------------
Stopped()144 bool vtkOpenGLRenderTimer::Stopped()
145 {
146 #ifndef NO_TIMESTAMP_QUERIES
147   return this->EndQuery != 0;
148 #else // NO_TIMESTAMP_QUERIES
149   return false;
150 #endif // NO_TIMESTAMP_QUERIES
151 }
152 
153 //------------------------------------------------------------------------------
Ready()154 bool vtkOpenGLRenderTimer::Ready()
155 {
156 #ifndef NO_TIMESTAMP_QUERIES
157   if (!this->IsSupported())
158   {
159     return false;
160   }
161 
162   if (!this->StartReady)
163   {
164     GLint ready;
165     glGetQueryObjectiv(static_cast<GLuint>(this->StartQuery),
166                        GL_QUERY_RESULT_AVAILABLE, &ready);
167     if (!ready)
168     {
169       return false;
170     }
171 
172     this->StartReady = true;
173     glGetQueryObjectui64v(static_cast<GLuint>(this->StartQuery),
174                           GL_QUERY_RESULT,
175                           reinterpret_cast<GLuint64*>(&this->StartTime));
176   }
177 
178   if (!this->EndReady)
179   {
180     GLint ready;
181     glGetQueryObjectiv(static_cast<GLuint>(this->EndQuery),
182                        GL_QUERY_RESULT_AVAILABLE, &ready);
183     if (!ready)
184     {
185       return false;
186     }
187 
188     this->EndReady = true;
189     glGetQueryObjectui64v(static_cast<GLuint>(this->EndQuery),
190                           GL_QUERY_RESULT,
191                           reinterpret_cast<GLuint64*>(&this->EndTime));
192   }
193 #endif // NO_TIMESTAMP_QUERIES
194 
195   return true;
196 }
197 
198 //------------------------------------------------------------------------------
GetElapsedSeconds()199 float vtkOpenGLRenderTimer::GetElapsedSeconds()
200 {
201 #ifndef NO_TIMESTAMP_QUERIES
202   if (!this->Ready())
203   {
204     return 0.f;
205   }
206 
207   return (this->EndTime - this->StartTime) * 1e-9f;
208 #else // NO_TIMESTAMP_QUERIES
209   return 0.f;
210 #endif // NO_TIMESTAMP_QUERIES
211 }
212 
213 //------------------------------------------------------------------------------
GetElapsedMilliseconds()214 float vtkOpenGLRenderTimer::GetElapsedMilliseconds()
215 {
216 #ifndef NO_TIMESTAMP_QUERIES
217   if (!this->Ready())
218   {
219     return 0.f;
220   }
221 
222   return (this->EndTime - this->StartTime) * 1e-6f;
223 #else // NO_TIMESTAMP_QUERIES
224   return 0.f;
225 #endif // NO_TIMESTAMP_QUERIES
226 }
227 
228 //------------------------------------------------------------------------------
229 vtkTypeUInt64
GetElapsedNanoseconds()230 vtkOpenGLRenderTimer::GetElapsedNanoseconds()
231 {
232 #ifndef NO_TIMESTAMP_QUERIES
233   if (!this->Ready())
234   {
235     return 0;
236   }
237 
238   return (this->EndTime - this->StartTime);
239 #else // NO_TIMESTAMP_QUERIES
240   return 0;
241 #endif // NO_TIMESTAMP_QUERIES
242 }
243 
244 //------------------------------------------------------------------------------
GetStartTime()245 vtkTypeUInt64 vtkOpenGLRenderTimer::GetStartTime()
246 {
247 #ifndef NO_TIMESTAMP_QUERIES
248   if (!this->Ready())
249   {
250     return 0;
251   }
252 
253   return this->StartTime;
254 #else // NO_TIMESTAMP_QUERIES
255   return 0;
256 #endif // NO_TIMESTAMP_QUERIES
257 }
258 
259 //------------------------------------------------------------------------------
GetStopTime()260 vtkTypeUInt64 vtkOpenGLRenderTimer::GetStopTime()
261 {
262 #ifndef NO_TIMESTAMP_QUERIES
263   if (!this->Ready())
264   {
265     return 0;
266   }
267 
268   return this->EndTime;
269 #else // NO_TIMESTAMP_QUERIES
270   return 0;
271 #endif // NO_TIMESTAMP_QUERIES
272 }
273 
274 //------------------------------------------------------------------------------
ReleaseGraphicsResources()275 void vtkOpenGLRenderTimer::ReleaseGraphicsResources()
276 {
277   this->Reset();
278 }
279 
280 //------------------------------------------------------------------------------
ReusableStart()281 void vtkOpenGLRenderTimer::ReusableStart()
282 {
283 #ifndef NO_TIMESTAMP_QUERIES
284   if (!this->IsSupported())
285   {
286     return;
287   }
288 
289   if (this->StartQuery == 0)
290   {
291     glGenQueries(1, static_cast<GLuint*>(&this->StartQuery));
292     glQueryCounter(static_cast<GLuint>(this->StartQuery), GL_TIMESTAMP);
293     this->ReusableStarted = true;
294     this->ReusableEnded = false;
295   }
296   if (!this->ReusableStarted)
297   {
298     glQueryCounter(static_cast<GLuint>(this->StartQuery), GL_TIMESTAMP);
299     this->ReusableStarted = true;
300     this->ReusableEnded = false;
301   }
302 #endif // NO_TIMESTAMP_QUERIES
303 }
304 
305 //------------------------------------------------------------------------------
ReusableStop()306 void vtkOpenGLRenderTimer::ReusableStop()
307 {
308 #ifndef NO_TIMESTAMP_QUERIES
309   if (!this->IsSupported())
310   {
311     return;
312   }
313 
314   if (!this->ReusableStarted)
315   {
316     vtkGenericWarningMacro("vtkOpenGLRenderTimer::ReusableStop called before "
317                            "vtkOpenGLRenderTimer::ReusableStart. Ignoring.");
318     return;
319   }
320 
321   if (this->EndQuery == 0)
322   {
323     glGenQueries(1, static_cast<GLuint*>(&this->EndQuery));
324     glQueryCounter(static_cast<GLuint>(this->EndQuery), GL_TIMESTAMP);
325     this->ReusableEnded = true;
326   }
327   if (!this->ReusableEnded)
328   {
329     glQueryCounter(static_cast<GLuint>(this->EndQuery), GL_TIMESTAMP);
330     this->ReusableEnded = true;
331   }
332 #endif // NO_TIMESTAMP_QUERIES
333 }
334 
335 //------------------------------------------------------------------------------
GetReusableElapsedSeconds()336 float vtkOpenGLRenderTimer::GetReusableElapsedSeconds()
337 {
338 #ifndef NO_TIMESTAMP_QUERIES
339   // we do not have an end query yet so we cannot have a time
340   if (!this->EndQuery)
341   {
342     return 0.0;
343   }
344 
345   if (this->ReusableStarted && !this->StartReady)
346   {
347     GLint ready;
348     glGetQueryObjectiv(static_cast<GLuint>(this->StartQuery),
349                        GL_QUERY_RESULT_AVAILABLE, &ready);
350     if (ready)
351     {
352      this->StartReady = true;
353     }
354   }
355 
356   if (this->StartReady && this->ReusableEnded && !this->EndReady)
357   {
358     GLint ready;
359     glGetQueryObjectiv(static_cast<GLuint>(this->EndQuery),
360                        GL_QUERY_RESULT_AVAILABLE, &ready);
361     if (ready)
362     {
363       this->EndReady = true;
364     }
365   }
366 
367   // if everything is ready read the times to get a new elapsed time
368   // and then prep for a new flight. This also has the benefit that
369   // if no one is getting the elapsed time then nothing is done
370   // beyond the first flight.
371   if (this->StartReady && this->EndReady)
372   {
373     glGetQueryObjectui64v(static_cast<GLuint>(this->StartQuery),
374                           GL_QUERY_RESULT,
375                           reinterpret_cast<GLuint64*>(&this->StartTime));
376     glGetQueryObjectui64v(static_cast<GLuint>(this->EndQuery),
377                           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