1// Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qopengltimerquery.h"
5
6#include "qopenglqueryhelper_p.h"
7#include <QtCore/private/qobject_p.h>
8#include <QtGui/QOpenGLContext>
9#include <QtGui/QOpenGLFunctions>
10
11QT_BEGIN_NAMESPACE
12
13// Helper class used as fallback if OpenGL <3.3 is being used with EXT_timer_query
14class QExtTimerQueryHelper
15{
16public:
17 QExtTimerQueryHelper(QOpenGLContext *context)
18 {
19 Q_ASSERT(context);
20 GetQueryObjectui64vEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLuint64EXT *)>(context->getProcAddress(procName: "glGetQueryObjectui64vEXT"));
21 GetQueryObjecti64vEXT = reinterpret_cast<void (QOPENGLF_APIENTRYP)(GLuint , GLenum , GLint64EXT *)>(context->getProcAddress(procName: "glGetQueryObjecti64vEXT"));
22 }
23
24 inline void glGetQueryObjectui64vEXT(GLuint id, GLenum pname, GLuint64EXT *params)
25 {
26 GetQueryObjectui64vEXT(id, pname, params);
27 }
28
29 inline void glGetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64EXT *params)
30 {
31 GetQueryObjecti64vEXT(id, pname, params);
32 }
33
34private:
35 void (QOPENGLF_APIENTRYP GetQueryObjectui64vEXT)(GLuint id, GLenum pname, GLuint64EXT *params);
36 void (QOPENGLF_APIENTRYP GetQueryObjecti64vEXT)(GLuint id, GLenum pname, GLint64EXT *params);
37};
38
39class QOpenGLTimerQueryPrivate : public QObjectPrivate
40{
41public:
42 QOpenGLTimerQueryPrivate()
43 : QObjectPrivate(),
44 context(nullptr),
45 ext(nullptr),
46 timeInterval(0),
47 timer(0)
48 {
49 }
50
51 ~QOpenGLTimerQueryPrivate()
52 {
53 delete core;
54 delete ext;
55 }
56
57 bool create();
58 void destroy();
59 void begin();
60 void end();
61 GLuint64 waitForTimeStamp() const;
62 void recordTimestamp();
63 bool isResultAvailable() const;
64 GLuint64 result() const;
65
66 // There are several cases we must handle:
67 // OpenGL >=3.3 includes timer queries as a core feature
68 // ARB_timer_query has same functionality as above. Requires OpenGL 3.2
69 // EXT_timer_query offers limited support. Can be used with OpenGL >=1.5
70 //
71 // Note that some implementations (OS X) provide OpenGL 3.2 but do not expose the
72 // ARB_timer_query extension. In such situations we must also be able to handle
73 // using the EXT_timer_query extension with any version of OpenGL.
74 //
75 // OpenGL 1.5 or above contains the generic query API and OpenGL 3.3 and
76 // ARB_timer_query provide the 64-bit query API. These are wrapped by
77 // QOpenGLQueryHelper. All we need to handle in addition is the EXT_timer_query
78 // case and to take care not to call the Core/ARB functions when we only
79 // have EXT_timer_query available.
80 QOpenGLContext *context;
81 QOpenGLQueryHelper *core;
82 QExtTimerQueryHelper *ext;
83 mutable GLuint64 timeInterval;
84 GLuint timer;
85};
86
87bool QOpenGLTimerQueryPrivate::create()
88{
89 QOpenGLContext *ctx = QOpenGLContext::currentContext();
90
91 if (timer && context == ctx)
92 return true;
93
94 context = ctx;
95 if (!context) {
96 qWarning(msg: "A current OpenGL context is required to create timer query objects");
97 return false;
98 }
99
100 if (context->isOpenGLES()) {
101 qWarning(msg: "QOpenGLTimerQuery: Not supported on OpenGL ES");
102 return false;
103 }
104
105 // Resolve the functions provided by OpenGL 1.5 and OpenGL 3.3 or ARB_timer_query
106 core = new QOpenGLQueryHelper(context);
107
108 // Check to see if we also need to resolve the functions for EXT_timer_query
109 QSurfaceFormat f = context->format();
110 if (f.version() <= qMakePair(value1: 3, value2: 2)
111 && !context->hasExtension(QByteArrayLiteral("GL_ARB_timer_query"))
112 && context->hasExtension(QByteArrayLiteral("GL_EXT_timer_query"))) {
113 ext = new QExtTimerQueryHelper(context);
114 } else if (f.version() <= qMakePair(value1: 3, value2: 2)
115 && !context->hasExtension(QByteArrayLiteral("GL_ARB_timer_query"))
116 && !context->hasExtension(QByteArrayLiteral("GL_EXT_timer_query"))) {
117 qWarning(msg: "QOpenGLTimerQuery requires one of:\n"
118 " OpenGL 3.3 or newer,\n"
119 " OpenGL 3.2 and the ARB_timer_query extension\n"
120 " or the EXT_timer query extension");
121 return false;
122 }
123
124 core->glGenQueries(n: 1, ids: &timer);
125 return (timer != 0);
126}
127
128void QOpenGLTimerQueryPrivate::destroy()
129{
130 if (!timer)
131 return;
132
133 core->glDeleteQueries(n: 1, ids: &timer);
134 timer = 0;
135 context = nullptr;
136}
137
138// GL_TIME_ELAPSED_EXT is not defined on OS X 10.6
139#if !defined(GL_TIME_ELAPSED_EXT)
140#define GL_TIME_ELAPSED_EXT 0x88BF
141#endif
142
143// GL_TIME_ELAPSED is not defined on OS X 10.7 or 10.8 yet
144#if !defined(GL_TIME_ELAPSED)
145#define GL_TIME_ELAPSED GL_TIME_ELAPSED_EXT
146#endif
147
148void QOpenGLTimerQueryPrivate::begin()
149{
150 core->glBeginQuery(GL_TIME_ELAPSED, id: timer);
151}
152
153void QOpenGLTimerQueryPrivate::end()
154{
155 core->glEndQuery(GL_TIME_ELAPSED);
156}
157
158void QOpenGLTimerQueryPrivate::recordTimestamp()
159{
160 // Don't call glQueryCounter if we only have EXT_timer_query
161#if defined(GL_TIMESTAMP)
162 if (!ext)
163 core->glQueryCounter(id: timer, GL_TIMESTAMP);
164 else
165 qWarning(msg: "QOpenGLTimerQuery::recordTimestamp() requires OpenGL 3.3 or GL_ARB_timer_query");
166#else
167 qWarning("QOpenGLTimerQuery::recordTimestamp() requires OpenGL 3.3 or GL_ARB_timer_query");
168#endif
169}
170
171GLuint64 QOpenGLTimerQueryPrivate::waitForTimeStamp() const
172{
173 GLint64 tmp = 0;
174#if defined(GL_TIMESTAMP)
175 if (!ext)
176 core->glGetInteger64v(GL_TIMESTAMP, params: &tmp);
177 else
178 qWarning(msg: "QOpenGLTimerQuery::waitForTimestamp() requires OpenGL 3.3 or GL_ARB_timer_query");
179#else
180 qWarning("QOpenGLTimerQuery::waitForTimestamp() requires OpenGL 3.3 or GL_ARB_timer_query");
181#endif
182 GLuint64 timestamp(tmp);
183 return timestamp;
184}
185
186bool QOpenGLTimerQueryPrivate::isResultAvailable() const
187{
188 GLuint available = GL_FALSE;
189 core->glGetQueryObjectuiv(id: timer, GL_QUERY_RESULT_AVAILABLE, params: &available);
190 return available;
191}
192
193GLuint64 QOpenGLTimerQueryPrivate::result() const
194{
195 if (!ext)
196 core->glGetQueryObjectui64v(id: timer, GL_QUERY_RESULT, params: &timeInterval);
197 else
198 ext->glGetQueryObjectui64vEXT(id: timer, GL_QUERY_RESULT, params: &timeInterval);
199 return timeInterval;
200}
201
202/*!
203 \class QOpenGLTimerQuery
204 \brief The QOpenGLTimerQuery class wraps an OpenGL timer query object.
205 \inmodule QtOpenGL
206 \since 5.1
207 \ingroup painting-3D
208
209 OpenGL timer query objects are OpenGL managed resources to measure the
210 execution times of sequences of OpenGL commands on the GPU.
211
212 OpenGL offers various levels of support for timer queries, depending on
213 the version of OpenGL you have and the presence of the ARB_timer_query or
214 EXT_timer_query extensions. The support can be summarized as:
215
216 \list
217 \li OpenGL >=3.3 offers full support for all timer query functionality.
218 \li OpenGL 3.2 with the ARB_timer_query extension offers full support
219 for all timer query functionality.
220 \li OpenGL <=3.2 with the EXT_timer_query extension offers limited support
221 in that the timestamp of the GPU cannot be queried. Places where this
222 impacts functions provided by Qt classes will be highlighted in the
223 function documentation.
224 \li OpenGL ES 2 (and OpenGL ES 3) do not provide any support for OpenGL
225 timer queries.
226 \endlist
227
228 OpenGL represents time with a granularity of 1 nanosecond (1e-9 seconds). As a
229 consequence of this, 32-bit integers would only give a total possible duration
230 of approximately 4 seconds, which would not be difficult to exceed in poorly
231 performing or lengthy operations. OpenGL therefore uses 64 bit integer types
232 to represent times. A GLuint64 variable has enough width to contain a duration
233 of hundreds of years, which is plenty for real-time rendering needs.
234
235 As with the other Qt OpenGL classes, QOpenGLTimerQuery has a create()
236 function to create the underlying OpenGL object. This is to allow the developer to
237 ensure that there is a valid current OpenGL context at the time.
238
239 Once created, timer queries can be issued in one of several ways. The simplest
240 method is to delimit a block of commands with calls to begin() and end(). This
241 instructs OpenGL to measure the time taken from completing all commands issued
242 prior to begin() until the completion of all commands issued prior to end().
243
244 At the end of a frame we can retrieve the results by calling waitForResult().
245 As this function's name implies, it blocks CPU execution until OpenGL notifies
246 that the timer query result is available. To avoid blocking, you can check
247 if the query result is available by calling isResultAvailable(). Note that
248 modern GPUs are deeply pipelined and query results may not become available for
249 between 1-5 frames after they were issued.
250
251 Note that OpenGL does not permit nesting or interleaving of multiple timer queries
252 using begin() and end(). Using multiple timer queries and recordTimestamp() avoids
253 this limitation. When using recordTimestamp() the result can be obtained at
254 some later time using isResultAvailable() and waitForResult(). Qt provides the
255 convenience class QOpenGLTimeMonitor that helps with using multiple query objects.
256
257 \sa QOpenGLTimeMonitor
258*/
259
260/*!
261 Creates a QOpenGLTimerQuery instance with the given \a parent. You must call create()
262 with a valid OpenGL context before using.
263*/
264QOpenGLTimerQuery::QOpenGLTimerQuery(QObject *parent)
265 : QObject(*new QOpenGLTimerQueryPrivate, parent)
266{
267}
268
269/*!
270 Destroys the QOpenGLTimerQuery and the underlying OpenGL resource.
271*/
272QOpenGLTimerQuery::~QOpenGLTimerQuery()
273{
274 QOpenGLContext* ctx = QOpenGLContext::currentContext();
275
276 Q_D(QOpenGLTimerQuery);
277 QOpenGLContext *oldContext = nullptr;
278 if (d->context != ctx) {
279 oldContext = ctx;
280 if (d->context->makeCurrent(surface: oldContext->surface())) {
281 ctx = d->context;
282 } else {
283 qWarning(msg: "QOpenGLTimerQuery::~QOpenGLTimerQuery() failed to make query objects's context current");
284 ctx = nullptr;
285 }
286 }
287
288 if (ctx)
289 destroy();
290
291 if (oldContext) {
292 if (!oldContext->makeCurrent(surface: oldContext->surface()))
293 qWarning(msg: "QOpenGLTimerQuery::~QOpenGLTimerQuery() failed to restore current context");
294 }
295}
296
297/*!
298 Creates the underlying OpenGL timer query object. There must be a valid OpenGL context
299 that supports query objects current for this function to succeed.
300
301 Returns \c true if the OpenGL timer query object was successfully created.
302*/
303bool QOpenGLTimerQuery::create()
304{
305 Q_D(QOpenGLTimerQuery);
306 return d->create();
307}
308
309/*!
310 Destroys the underlying OpenGL timer query object. The context that was current when
311 create() was called must be current when calling this function.
312*/
313void QOpenGLTimerQuery::destroy()
314{
315 Q_D(QOpenGLTimerQuery);
316 d->destroy();
317}
318
319/*!
320 Returns \c true if the underlying OpenGL query object has been created. If this
321 returns \c true and the associated OpenGL context is current, then you are able to issue
322 queries with this object.
323*/
324bool QOpenGLTimerQuery::isCreated() const
325{
326 Q_D(const QOpenGLTimerQuery);
327 return (d->timer != 0);
328}
329
330/*!
331 Returns the id of the underlying OpenGL query object.
332*/
333GLuint QOpenGLTimerQuery::objectId() const
334{
335 Q_D(const QOpenGLTimerQuery);
336 return d->timer;
337}
338
339/*!
340 Marks the start point in the OpenGL command queue for a sequence of commands to
341 be timed by this query object.
342
343 This is useful for simple use-cases. Usually it is better to use recordTimestamp().
344
345 \sa end(), isResultAvailable(), waitForResult(), recordTimestamp()
346*/
347void QOpenGLTimerQuery::begin()
348{
349 Q_D(QOpenGLTimerQuery);
350 d->begin();
351}
352
353/*!
354 Marks the end point in the OpenGL command queue for a sequence of commands to
355 be timed by this query object.
356
357 This is useful for simple use-cases. Usually it is better to use recordTimestamp().
358
359 \sa begin(), isResultAvailable(), waitForResult(), recordTimestamp()
360*/
361void QOpenGLTimerQuery::end()
362{
363 Q_D(QOpenGLTimerQuery);
364 d->end();
365}
366
367/*!
368 Places a marker in the OpenGL command queue for the GPU to record the timestamp
369 when this marker is reached by the GPU. This function is non-blocking and the
370 result will become available at some later time.
371
372 The availability of the result can be checked with isResultAvailable(). The result
373 can be fetched with waitForResult() which will block if the result is not yet
374 available.
375
376 \sa waitForResult(), isResultAvailable(), begin(), end()
377*/
378void QOpenGLTimerQuery::recordTimestamp()
379{
380 Q_D(QOpenGLTimerQuery);
381 return d->recordTimestamp();
382}
383
384/*!
385 Returns the current timestamp of the GPU when all previously issued OpenGL
386 commands have been received but not necessarily executed by the GPU.
387
388 This function blocks until the result is returned.
389
390 \sa recordTimestamp()
391*/
392GLuint64 QOpenGLTimerQuery::waitForTimestamp() const
393{
394 Q_D(const QOpenGLTimerQuery);
395 return d->waitForTimeStamp();
396}
397
398/*!
399 Returns \c true if the OpenGL timer query result is available.
400
401 This function is non-blocking and ideally should be used to check for the
402 availability of the query result before calling waitForResult().
403
404 \sa waitForResult()
405*/
406bool QOpenGLTimerQuery::isResultAvailable() const
407{
408 Q_D(const QOpenGLTimerQuery);
409 return d->isResultAvailable();
410}
411
412/*!
413 Returns the result of the OpenGL timer query.
414
415 This function will block until the result is made available by OpenGL. It is
416 recommended to call isResultAvailable() to ensure that the result is available
417 to avoid unnecessary blocking and stalling.
418
419 \sa isResultAvailable()
420*/
421GLuint64 QOpenGLTimerQuery::waitForResult() const
422{
423 Q_D(const QOpenGLTimerQuery);
424 return d->result();
425}
426
427
428class QOpenGLTimeMonitorPrivate : public QObjectPrivate
429{
430public:
431 QOpenGLTimeMonitorPrivate()
432 : QObjectPrivate(),
433 timers(),
434 timeSamples(),
435 context(nullptr),
436 core(nullptr),
437 ext(nullptr),
438 requestedSampleCount(2),
439 currentSample(-1),
440 timerQueryActive(false)
441 {
442 }
443
444 ~QOpenGLTimeMonitorPrivate()
445 {
446 delete core;
447 delete ext;
448 }
449
450 bool create();
451 void destroy();
452 void recordSample();
453 bool isResultAvailable() const;
454 QList<GLuint64> samples() const;
455 QList<GLuint64> intervals() const;
456 void reset();
457
458 QList<GLuint> timers;
459 mutable QList<GLuint64> timeSamples;
460
461 QOpenGLContext *context;
462 QOpenGLQueryHelper *core;
463 QExtTimerQueryHelper *ext;
464
465 int requestedSampleCount;
466 int currentSample;
467 mutable bool timerQueryActive;
468};
469
470bool QOpenGLTimeMonitorPrivate::create()
471{
472 if (!timers.isEmpty() && timers.at(i: 0) != 0 && timers.size() == requestedSampleCount)
473 return true;
474
475 QOpenGLContext *ctx = QOpenGLContext::currentContext();
476 if (context && context != ctx) {
477 qWarning(msg: "QTimeMonitor: Attempting to use different OpenGL context to recreate timers.\n"
478 "Please call destroy() first or use the same context to previously create");
479 return false;
480 }
481
482 context = ctx;
483 if (!context) {
484 qWarning(msg: "A current OpenGL context is required to create timer query objects");
485 return false;
486 }
487
488 // Resize the vectors that hold the timers and the recorded samples
489 timers.resize(size: requestedSampleCount);
490 timeSamples.resize(size: requestedSampleCount);
491
492 // Resolve the functions provided by OpenGL 1.5 and OpenGL 3.3 or ARB_timer_query
493 core = new QOpenGLQueryHelper(context);
494
495 // Check to see if we also need to resolve the functions for EXT_timer_query
496 QSurfaceFormat f = context->format();
497 if (f.version() <= qMakePair(value1: 3, value2: 2)
498 && !context->hasExtension(QByteArrayLiteral("GL_ARB_timer_query"))
499 && context->hasExtension(QByteArrayLiteral("GL_EXT_timer_query"))) {
500 ext = new QExtTimerQueryHelper(context);
501 } else if (f.version() <= qMakePair(value1: 3, value2: 2)
502 && !context->hasExtension(QByteArrayLiteral("GL_ARB_timer_query"))
503 && !context->hasExtension(QByteArrayLiteral("GL_EXT_timer_query"))) {
504 qWarning(msg: "QOpenGLTimeMonitor requires one of:\n"
505 " OpenGL 3.3 or newer,\n"
506 " OpenGL 3.2 and the ARB_timer_query extension\n"
507 " or the EXT_timer query extension");
508 return false;
509 }
510
511 core->glGenQueries(n: requestedSampleCount, ids: timers.data());
512 return (timers.at(i: 0) != 0);
513}
514
515void QOpenGLTimeMonitorPrivate::destroy()
516{
517 if (timers.isEmpty() || timers.at(i: 0) == 0)
518 return;
519
520 core->glDeleteQueries(n: timers.size(), ids: timers.data());
521 timers.clear();
522 delete core;
523 core = nullptr;
524 delete ext;
525 ext = nullptr;
526 context = nullptr;
527}
528
529void QOpenGLTimeMonitorPrivate::recordSample()
530{
531 // Use glQueryCounter() and GL_TIMESTAMP where available.
532 // Otherwise, simulate it with glBeginQuery()/glEndQuery()
533 if (!ext) {
534#if defined(GL_TIMESTAMP)
535 core->glQueryCounter(id: timers.at(i: ++currentSample), GL_TIMESTAMP);
536#endif
537 } else {
538 if (currentSample == -1) {
539 core->glBeginQuery(GL_TIME_ELAPSED_EXT, id: timers.at(i: ++currentSample));
540 timerQueryActive = true;
541 } else if (currentSample < timers.size() - 1) {
542 core->glEndQuery(GL_TIME_ELAPSED_EXT);
543 core->glBeginQuery(GL_TIME_ELAPSED_EXT, id: timers.at(i: ++currentSample));
544 } else {
545 if (timerQueryActive) {
546 core->glEndQuery(GL_TIME_ELAPSED_EXT);
547 timerQueryActive = false;
548 }
549 }
550 }
551}
552
553bool QOpenGLTimeMonitorPrivate::isResultAvailable() const
554{
555 // The OpenGL spec says that if a query result is ready then the results of all queries
556 // of the same type issued before it must also be ready. Therefore we only need to check
557 // the availability of the result for the last issued query
558 GLuint available = GL_FALSE;
559 core->glGetQueryObjectuiv(id: timers.at(i: currentSample), GL_QUERY_RESULT_AVAILABLE, params: &available);
560 return available;
561}
562
563QList<GLuint64> QOpenGLTimeMonitorPrivate::samples() const
564{
565 // For the Core and ARB options just ask for the timestamp for each timer query.
566 // For the EXT implementation we cannot obtain timestamps so we defer any result
567 // collection to the intervals() function
568 if (!ext) {
569 for (int i = 0; i <= currentSample; ++i)
570 core->glGetQueryObjectui64v(id: timers.at(i), GL_QUERY_RESULT, params: &timeSamples[i]);
571 } else {
572 qWarning(msg: "QOpenGLTimeMonitor::samples() requires OpenGL >=3.3\n"
573 "or OpenGL 3.2 and GL_ARB_timer_query");
574 }
575 return timeSamples;
576}
577
578QList<GLuint64> QOpenGLTimeMonitorPrivate::intervals() const
579{
580 QList<GLuint64> intervals(timers.size() - 1);
581 if (!ext) {
582 // Obtain the timestamp samples and calculate the interval durations
583 const QList<GLuint64> timeStamps = samples();
584 for (int i = 0; i < intervals.size(); ++i)
585 intervals[i] = timeStamps[i+1] - timeStamps[i];
586 } else {
587 // Stop the last timer if needed
588 if (timerQueryActive) {
589 core->glEndQuery(GL_TIME_ELAPSED_EXT);
590 timerQueryActive = false;
591 }
592
593 // Obtain the results from all timers apart from the redundant last one. In this
594 // case the results actually are the intervals not timestamps
595 for (int i = 0; i < currentSample; ++i)
596 ext->glGetQueryObjectui64vEXT(id: timers.at(i), GL_QUERY_RESULT, params: &intervals[i]);
597 }
598
599 return intervals;
600}
601
602void QOpenGLTimeMonitorPrivate::reset()
603{
604 currentSample = -1;
605 timeSamples.fill(t: 0);
606}
607
608
609/*!
610 \class QOpenGLTimeMonitor
611 \brief The QOpenGLTimeMonitor class wraps a sequence of OpenGL timer query objects.
612 \inmodule QtOpenGL
613 \since 5.1
614 \ingroup painting-3D
615
616 The QOpenGLTimeMonitor class is a convenience wrapper around a collection of OpenGL
617 timer query objects used to measure intervals of time on the GPU to the level of
618 granularity required by your rendering application.
619
620 The OpenGL timer queries objects are queried in sequence to record the GPU
621 timestamps at positions of interest in your rendering code. Once the results for
622 all issues timer queries become available, the results can be fetched and
623 QOpenGLTimerMonitor will calculate the recorded time intervals for you.
624
625 The typical use case of this class is to either profile your application's rendering
626 algorithms or to adjust those algorithms in real-time for dynamic performance/quality
627 balancing.
628
629 Prior to using QOpenGLTimeMonitor in your rendering function you should set the
630 required number of sample points that you wish to record by calling setSamples(). Note
631 that measuring N sample points will produce N-1 time intervals. Once you have set the
632 number of sample points, call the create() function with a valid current OpenGL context
633 to create the necessary query timer objects. These steps are usually performed just
634 once in an initialization function.
635
636 Use the recordSample() function to delimit blocks of code containing OpenGL commands
637 that you wish to time. You can check availability of the resulting time
638 samples and time intervals with isResultAvailable(). The calculated time intervals and
639 the raw timestamp samples can be retrieved with the blocking waitForIntervals() and
640 waitForSamples() functions respectively.
641
642 After retrieving the results and before starting a new round of taking samples
643 (for example, in the next frame) be sure to call the reset() function which will clear
644 the cached results and reset the timer index back to the first timer object.
645
646 \sa QOpenGLTimerQuery
647*/
648
649/*!
650 Creates a QOpenGLTimeMonitor instance with the given \a parent. You must call create()
651 with a valid OpenGL context before using.
652
653 \sa setSampleCount(), create()
654*/
655QOpenGLTimeMonitor::QOpenGLTimeMonitor(QObject *parent)
656 : QObject(*new QOpenGLTimeMonitorPrivate, parent)
657{
658}
659
660/*!
661 Destroys the QOpenGLTimeMonitor and any underlying OpenGL resources.
662*/
663QOpenGLTimeMonitor::~QOpenGLTimeMonitor()
664{
665 QOpenGLContext* ctx = QOpenGLContext::currentContext();
666
667 Q_D(QOpenGLTimeMonitor);
668 QOpenGLContext *oldContext = nullptr;
669 if (d->context != ctx) {
670 oldContext = ctx;
671 if (d->context->makeCurrent(surface: oldContext->surface())) {
672 ctx = d->context;
673 } else {
674 qWarning(msg: "QOpenGLTimeMonitor::~QOpenGLTimeMonitor() failed to make time monitor's context current");
675 ctx = nullptr;
676 }
677 }
678
679 if (ctx)
680 destroy();
681
682 if (oldContext) {
683 if (!oldContext->makeCurrent(surface: oldContext->surface()))
684 qWarning(msg: "QOpenGLTimeMonitor::~QOpenGLTimeMonitor() failed to restore current context");
685 }
686}
687
688/*!
689 Sets the number of sample points to \a sampleCount. After setting the number
690 of samples with this function, you must call create() to instantiate the underlying
691 OpenGL timer query objects.
692
693 The new \a sampleCount must be at least 2.
694
695 \sa sampleCount(), create(), recordSample()
696*/
697void QOpenGLTimeMonitor::setSampleCount(int sampleCount)
698{
699 // We need at least 2 samples to get an interval
700 if (sampleCount < 2)
701 return;
702 Q_D(QOpenGLTimeMonitor);
703 d->requestedSampleCount = sampleCount;
704}
705
706/*!
707 Returns the number of sample points that have been requested with
708 setSampleCount(). If create was successfully called following setSampleCount(),
709 then the value returned will be the actual number of sample points
710 that can be used.
711
712 The default value for sample count is 2, leading to the measurement of a
713 single interval.
714
715 \sa setSampleCount()
716*/
717int QOpenGLTimeMonitor::sampleCount() const
718{
719 Q_D(const QOpenGLTimeMonitor);
720 return d->requestedSampleCount;
721}
722
723/*!
724 Instantiate sampleCount() OpenGL timer query objects that will be used
725 to track the amount of time taken to execute OpenGL commands between
726 successive calls to recordSample().
727
728 Returns \c true if the OpenGL timer query objects could be created.
729
730 \sa destroy(), setSampleCount(), recordSample()
731*/
732bool QOpenGLTimeMonitor::create()
733{
734 Q_D(QOpenGLTimeMonitor);
735 return d->create();
736}
737
738/*!
739 Destroys any OpenGL timer query objects used within this instance.
740
741 \sa create()
742*/
743void QOpenGLTimeMonitor::destroy()
744{
745 Q_D(QOpenGLTimeMonitor);
746 d->destroy();
747}
748
749/*!
750 Returns \c true if the underlying OpenGL query objects have been created. If this
751 returns \c true and the associated OpenGL context is current, then you are able to record
752 time samples with this object.
753*/
754bool QOpenGLTimeMonitor::isCreated() const
755{
756 Q_D(const QOpenGLTimeMonitor);
757 return (!d->timers.isEmpty() && d->timers.at(i: 0) != 0);
758}
759
760/*!
761 Returns a QList containing the object Ids of the OpenGL timer query objects.
762*/
763QList<GLuint> QOpenGLTimeMonitor::objectIds() const
764{
765 Q_D(const QOpenGLTimeMonitor);
766 return d->timers;
767}
768
769/*!
770 Issues an OpenGL timer query at this point in the OpenGL command queue. Calling this
771 function in a sequence in your application's rendering function, will build up
772 details of the GPU time taken to execute the OpenGL commands between successive
773 calls to this function.
774
775 \sa setSampleCount(), isResultAvailable(), waitForSamples(), waitForIntervals()
776*/
777int QOpenGLTimeMonitor::recordSample()
778{
779 Q_D(QOpenGLTimeMonitor);
780 d->recordSample();
781 return d->currentSample;
782}
783
784/*!
785 Returns \c true if the OpenGL timer query results are available.
786
787 \sa waitForSamples(), waitForIntervals()
788*/
789bool QOpenGLTimeMonitor::isResultAvailable() const
790{
791 Q_D(const QOpenGLTimeMonitor);
792 return d->isResultAvailable();
793}
794
795/*!
796 Returns a QList containing the GPU timestamps taken with recordSample().
797
798 This function will block until OpenGL indicates the results are available. It
799 is recommended to check the availability of the result prior to calling this
800 function with isResultAvailable().
801
802 \note This function only works on systems that have OpenGL >=3.3 or the
803 ARB_timer_query extension. See QOpenGLTimerQuery for more details.
804
805 \sa waitForIntervals(), isResultAvailable()
806*/
807QList<GLuint64> QOpenGLTimeMonitor::waitForSamples() const
808{
809 Q_D(const QOpenGLTimeMonitor);
810 return d->samples();
811}
812
813/*!
814 Returns a QList containing the time intervals delimited by the calls to
815 recordSample(). The resulting vector will contain one fewer element as
816 this represents the intervening intervals rather than the actual timestamp
817 samples.
818
819 This function will block until OpenGL indicates the results are available. It
820 is recommended to check the availability of the result prior to calling this
821 function with isResultAvailable().
822
823 \sa waitForSamples(), isResultAvailable()
824*/
825QList<GLuint64> QOpenGLTimeMonitor::waitForIntervals() const
826{
827 Q_D(const QOpenGLTimeMonitor);
828 return d->intervals();
829}
830
831/*!
832 Resets the time monitor ready for use in another frame of rendering. Call
833 this once you have obtained the previous results and before calling
834 recordSample() for the first time on the next frame.
835
836 \sa recordSample()
837*/
838void QOpenGLTimeMonitor::reset()
839{
840 Q_D(QOpenGLTimeMonitor);
841 d->reset();
842}
843
844QT_END_NAMESPACE
845
846#include "moc_qopengltimerquery.cpp"
847

source code of qtbase/src/opengl/qopengltimerquery.cpp