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