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

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