1/****************************************************************************
2**
3** Copyright (C) 2013 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
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 <QtCore/private/qobject_p.h>
41#include <QtCore/qglobal.h>
42#include <QtCore/qvarlengtharray.h>
43#include <QtGui/qopengl.h>
44#include <QtGui/qopenglfunctions.h>
45#include <QtGui/qoffscreensurface.h>
46
47#include "qopengldebug.h"
48
49QT_BEGIN_NAMESPACE
50
51/*!
52 \class QOpenGLDebugMessage
53 \brief The QOpenGLDebugMessage class wraps an OpenGL debug message.
54 \inmodule QtGui
55 \reentrant
56 \since 5.1
57 \ingroup shared
58 \ingroup painting-3D
59
60 Debug messages are usually created by the OpenGL server and then read by
61 OpenGL clients (either from the OpenGL internal debug log, or logged in real-time).
62 A debug message has a textual representation, a vendor-specific numeric id,
63 a source, a type and a severity.
64
65 It's also possible for applications or third-party libraries and toolkits
66 to create and insert messages in the debug log. In order to do so, you can use
67 the createApplicationMessage() or the createThirdPartyMessage() static functions.
68
69 \sa QOpenGLDebugLogger
70*/
71
72/*!
73 \class QOpenGLDebugLogger
74 \brief The QOpenGLDebugLogger enables logging of OpenGL debugging messages.
75 \inmodule QtGui
76 \since 5.1
77 \ingroup painting-3D
78
79 \tableofcontents
80
81 \section1 Introduction
82
83 OpenGL programming can be very error prone. Most of the time, a single
84 failing call to OpenGL can cause an entire portion of an application to
85 stop working, with nothing being drawn on the screen.
86
87 The only way to be sure that no errors are being returned from the OpenGL
88 implementation is checking with \c{glGetError} after each and every API
89 call. Moreover, OpenGL errors stack up, therefore glGetError should always
90 be used in a loop like this:
91
92 \snippet code/src_gui_opengl_qopengldebug.cpp 0
93
94 If you try to clear the error stack, make sure not just keep going until
95 GL_NO_ERROR is returned but also break on GL_CONTEXT_LOST as that error
96 value will keep repeating.
97
98 There are also many other information we are interested in (as application
99 developers), for instance performance issues, or warnings about using
100 deprecated APIs. Those kind of messages are not reported through the
101 ordinary OpenGL error reporting mechanisms.
102
103 QOpenGLDebugLogger aims at addressing these issues by providing access to
104 the \e{OpenGL debug log}. If your OpenGL implementation supports it (by
105 exposing the \c{GL_KHR_debug} extension), messages from the OpenGL server
106 will be either logged in an internal OpenGL log, or passed in "real-time"
107 to listeners as they're generated from OpenGL.
108
109 QOpenGLDebugLogger supports both these modes of operation. Refer to the
110 following sections to find out the differences between them.
111
112 \section1 Creating an OpenGL Debug Context
113
114 For efficiency reasons, OpenGL implementations are allowed not to create
115 any debug output at all, unless the OpenGL context is a debug context. In order
116 to create a debug context from Qt, you must set the QSurfaceFormat::DebugContext
117 format option on the QSurfaceFormat used to create the QOpenGLContext object:
118
119 \snippet code/src_gui_opengl_qopengldebug.cpp 1
120
121 Note that requesting a 3.2 OpenGL Core Profile is just for the example's
122 purposes; this class is not tied to any specific OpenGL or OpenGL ES
123 version, as it relies on the availability of the \c{GL_KHR_debug} extension
124 (see below).
125
126 \section1 Creating and Initializing a QOpenGLDebugLogger
127
128 QOpenGLDebugLogger is a simple QObject-derived class. Just like all QObject
129 subclasses, you create an instance (and optionally specify a parent
130 object), and like the other OpenGL functions in Qt you \e{must} initialize
131 it before usage by calling initialize() whilst there is a current OpenGL context:
132
133 \snippet code/src_gui_opengl_qopengldebug.cpp 2
134
135 Note that the \c{GL_KHR_debug} extension \e{must} be available in the context
136 in order to access the messages logged by OpenGL. You can check the
137 presence of this extension by calling:
138
139 \snippet code/src_gui_opengl_qopengldebug.cpp 3
140
141 where \c{ctx} is a valid QOpenGLContext. If the extension is not available,
142 initialize() will return false.
143
144 \section1 Reading the Internal OpenGL Debug Log
145
146 OpenGL implementations keep an internal log of debug messages. Messages
147 stored in this log can be retrieved by using the loggedMessages() function:
148
149 \snippet code/src_gui_opengl_qopengldebug.cpp 4
150
151 The internal log has a limited size; when it fills up, older messages will
152 get discarded to make room for the new incoming messages. When you call
153 loggedMessages(), the internal log will be emptied as well.
154
155 If you want to be sure not to lose any debug message, you must use real-time
156 logging instead of calling this function. However, debug messages might
157 still be generated in the timespan between context creation and activation
158 of real-time logging (or, in general, when the real-time logging is disabled).
159
160 \section1 Real-time logging of messages
161
162 It is also possible to receive a stream of debug messages from the OpenGL
163 server \e{as they are generated} by the implementation. In order to do so,
164 you need to connect a suitable slot to the messageLogged() signal, and
165 start logging by calling startLogging():
166
167 \snippet code/src_gui_opengl_qopengldebug.cpp 5
168
169 Similarly, logging can be disabled at any time by calling the stopLogging()
170 function.
171
172 Real-time logging can be either asynchronous or synchronous, depending on
173 the parameter passed to startLogging(). When logging in asynchronous mode
174 (the default, as it has a very small overhead), the OpenGL implementation
175 can generate messages at any time, and/or in an order which is different from the
176 order of the OpenGL commands which caused those messages to be logged.
177 The messages could also be generated from a thread that it's different from
178 the thread the context is currently bound to. This is because OpenGL
179 implementations are usually highly threaded and asynchronous, and therefore
180 no warranties are made about the relative order and the timings of the
181 debug messages.
182
183 On the other hand, logging in synchronous mode has a high overhead, but
184 the OpenGL implementation guarantees that all the messages caused by a
185 certain command are received in order, before the command returns,
186 and from the same thread the OpenGL context is bound to.
187
188 This means that when logging in synchronous mode you will be able to run
189 your OpenGL application in a debugger, put a breakpoint on a slot connected
190 to the messageLogged() signal, and see in the backtrace the exact call
191 that caused the logged message. This can be extremely useful to debug
192 an OpenGL problem. Note that if OpenGL rendering is happening in another
193 thread, you must force the signal/slot connection type to Qt::DirectConnection
194 in order to be able to see the actual backtrace.
195
196 Refer to the LoggingMode enum documentation for more information about
197 logging modes.
198
199 \note When real-time logging is enabled, debug messages will \e{not} be
200 inserted in the internal OpenGL debug log any more; messages already
201 present in the internal log will not be deleted, nor they will be emitted
202 through the messageLogged() signal. Since some messages might be generated
203 before real-time logging is started (and therefore be kept in the internal
204 OpenGL log), it is important to always check if it contains any message
205 after calling startLogging().
206
207 \section1 Inserting Messages in the Debug Log
208
209 It is possible for applications and libraries to insert custom messages in
210 the debug log, for instance for marking a group of related OpenGL commands
211 and therefore being then able to identify eventual messages coming from them.
212
213 In order to do so, you can create a QOpenGLDebugMessage object by calling
214 \l{QOpenGLDebugMessage::}{createApplicationMessage()} or
215 \l{QOpenGLDebugMessage::}{createThirdPartyMessage()}, and then inserting it
216 into the log by calling logMessage():
217
218 \snippet code/src_gui_opengl_qopengldebug.cpp 6
219
220 Note that OpenGL implementations have a vendor-specific limit to the length
221 of the messages that can be inserted in the debug log. You can retrieve
222 this length by calling the maximumMessageLength() method; messages longer
223 than the limit will automatically get truncated.
224
225 \section1 Controlling the Debug Output
226
227 QOpenGLDebugMessage is also able to apply filters to the debug messages, and
228 therefore limit the amount of messages logged. You can enable or disable
229 logging of messages by calling enableMessages() and disableMessages()
230 respectively. By default, all messages are logged.
231
232 It is possible to enable or disable messages by selecting them by:
233
234 \list
235 \li source, type and severity (and including all ids in the selection);
236 \li id, source and type (and including all severities in the selection).
237 \endlist
238
239 Note that the "enabled" status for a given message is a property of the
240 (id, source, type, severity) tuple; the message attributes \e{do not} form
241 a hierarchy of any kind. You should be careful about the order of the calls
242 to enableMessages() and disableMessages(), as it will change which
243 messages will are enabled / disabled.
244
245 It's not possible to filter by the message text itself; applications
246 have to do that on their own (in slots connected to the messageLogged()
247 signal, or after fetching the messages in the internal debug log
248 through loggedMessages()).
249
250 In order to simplify the management of the enabled / disabled statuses,
251 QOpenGLDebugMessage also supports the concept of \c{debug groups}. A debug
252 group contains the group of enabled / disabled configurations of debug
253 messages. Moreover, debug groups are organized in a stack: it is possible
254 to push and pop groups by calling pushGroup() and popGroup() respectively.
255 (When an OpenGL context is created, there is already a group in the stack).
256
257 The enableMessages() and disableMessages() functions will modify the
258 configuration in the current debug group, that is, the one at the top of
259 the debug groups stack.
260
261 When a new group is pushed onto the debug groups stack, it will inherit
262 the configuration of the group that was previously on the top of the stack.
263 Vice versa, popping a debug group will restore the configuration of
264 the debug group that becomes the new top.
265
266 Pushing (respectively popping) debug groups will also automatically generate
267 a debug message of type QOpenGLDebugMessage::GroupPushType (respectively
268 \l{QOpenGLDebugMessage::}{GroupPopType}).
269
270 \sa QOpenGLDebugMessage
271*/
272
273/*!
274 \enum QOpenGLDebugMessage::Source
275
276 The Source enum defines the source of the debug message.
277
278 \value InvalidSource
279 The source of the message is invalid; this is the source of a
280 default-constructed QOpenGLDebugMessage object.
281
282 \value APISource
283 The message was generated in response to OpenGL API calls.
284
285 \value WindowSystemSource
286 The message was generated by the window system.
287
288 \value ShaderCompilerSource
289 The message was generated by the shader compiler.
290
291 \value ThirdPartySource
292 The message was generated by a third party, for instance an OpenGL
293 framework a or debugging toolkit.
294
295 \value ApplicationSource
296 The message was generated by the application itself.
297
298 \value OtherSource
299 The message was generated by a source not included in this
300 enumeration.
301
302 \omitvalue LastSource
303
304 \value AnySource
305 This value corresponds to a mask of all possible message sources.
306*/
307
308/*!
309 \enum QOpenGLDebugMessage::Type
310
311 The Type enum defines the type of the debug message.
312
313 \value InvalidType
314 The type of the message is invalid; this is the type of a
315 default-constructed QOpenGLDebugMessage object.
316
317 \value ErrorType
318 The message represents an error.
319
320 \value DeprecatedBehaviorType
321 The message represents an usage of deprecated behavior.
322
323 \value UndefinedBehaviorType
324 The message represents an usage of undefined behavior.
325
326 \value PortabilityType
327 The message represents an usage of vendor-specific behavior,
328 that might pose portability concerns.
329
330 \value PerformanceType
331 The message represents a performance issue.
332
333 \value OtherType
334 The message represents a type not included in this
335 enumeration.
336
337 \value MarkerType
338 The message represents a marker in the debug log.
339
340 \value GroupPushType
341 The message represents a debug group push operation.
342
343 \value GroupPopType
344 The message represents a debug group pop operation.
345
346 \omitvalue LastType
347
348 \value AnyType
349 This value corresponds to a mask of all possible message types.
350*/
351
352/*!
353 \enum QOpenGLDebugMessage::Severity
354
355 The Severity enum defines the severity of the debug message.
356
357 \value InvalidSeverity
358 The severity of the message is invalid; this is the severity of a
359 default-constructed QOpenGLDebugMessage object.
360
361 \value HighSeverity
362 The message has a high severity.
363
364 \value MediumSeverity
365 The message has a medium severity.
366
367 \value LowSeverity
368 The message has a low severity.
369
370 \value NotificationSeverity
371 The message is a notification.
372
373 \omitvalue LastSeverity
374
375 \value AnySeverity
376 This value corresponds to a mask of all possible message severities.
377*/
378
379/*!
380 \property QOpenGLDebugLogger::loggingMode
381
382 \brief the logging mode passed to startLogging().
383
384 Note that logging must have been started or the value of this property
385 will be meaningless.
386
387 \sa startLogging(), isLogging()
388*/
389/*!
390 \enum QOpenGLDebugLogger::LoggingMode
391
392 The LoggingMode enum defines the logging mode of the logger object.
393
394 \value AsynchronousLogging
395 Messages from the OpenGL server are logged asynchronously. This means
396 that messages can be logged some time after the corresponding OpenGL
397 actions that caused them, and even be received in an out-of-order
398 fashion, depending on the OpenGL implementation. This mode has a very low
399 performance penalty, as OpenGL implementations are heavily threaded
400 and asynchronous by nature.
401
402 \value SynchronousLogging
403 Messages from the OpenGL server are logged synchronously and
404 sequentially. This has a severe performance hit, as OpenGL
405 implementations are very asynchronous by nature; but it's very useful
406 to debug OpenGL problems, as OpenGL guarantees that the messages
407 generated by a OpenGL command will be logged before the corresponding
408 command execution has returned. Therefore, you can install a breakpoint
409 on the messageLogged() signal and see in the backtrace which OpenGL
410 command caused it; the only caveat is that if you are using OpenGL from
411 multiple threads you may need to force direct connection when
412 connecting to the messageLogged() signal.
413*/
414
415// When using OpenGL ES 2.0, all the necessary GL_KHR_debug constants are
416// provided in qopengles2ext.h. Unfortunately, newer versions of that file
417// suffix everything with _KHR which causes extra headache when the goal is
418// to have a single piece of code that builds in all our target
419// environments. Therefore, try to detect this and use our custom defines
420// instead, which we anyway need for OS X.
421
422#if defined(GL_KHR_debug) && defined(GL_DEBUG_SOURCE_API_KHR)
423#define USE_MANUAL_DEFS
424#endif
425
426// Under OSX (at least up to 10.8) we cannot include our copy of glext.h,
427// but we use the system-wide one, which unfortunately lacks all the needed
428// defines/typedefs. In order to make the code compile, we just add here
429// the GL_KHR_debug defines.
430
431#ifndef GL_KHR_debug
432#define GL_KHR_debug 1
433#define USE_MANUAL_DEFS
434#endif
435
436#ifdef USE_MANUAL_DEFS
437
438#ifndef GL_DEBUG_OUTPUT_SYNCHRONOUS
439#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242
440#endif
441#ifndef GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH
442#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243
443#endif
444#ifndef GL_DEBUG_CALLBACK_FUNCTION
445#define GL_DEBUG_CALLBACK_FUNCTION 0x8244
446#endif
447#ifndef GL_DEBUG_CALLBACK_USER_PARAM
448#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245
449#endif
450#ifndef GL_DEBUG_SOURCE_API
451#define GL_DEBUG_SOURCE_API 0x8246
452#endif
453#ifndef GL_DEBUG_SOURCE_WINDOW_SYSTEM
454#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247
455#endif
456#ifndef GL_DEBUG_SOURCE_SHADER_COMPILER
457#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248
458#endif
459#ifndef GL_DEBUG_SOURCE_THIRD_PARTY
460#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249
461#endif
462#ifndef GL_DEBUG_SOURCE_APPLICATION
463#define GL_DEBUG_SOURCE_APPLICATION 0x824A
464#endif
465#ifndef GL_DEBUG_SOURCE_OTHER
466#define GL_DEBUG_SOURCE_OTHER 0x824B
467#endif
468#ifndef GL_DEBUG_TYPE_ERROR
469#define GL_DEBUG_TYPE_ERROR 0x824C
470#endif
471#ifndef GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR
472#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D
473#endif
474#ifndef GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR
475#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E
476#endif
477#ifndef GL_DEBUG_TYPE_PORTABILITY
478#define GL_DEBUG_TYPE_PORTABILITY 0x824F
479#endif
480#ifndef GL_DEBUG_TYPE_PERFORMANCE
481#define GL_DEBUG_TYPE_PERFORMANCE 0x8250
482#endif
483#ifndef GL_DEBUG_TYPE_OTHER
484#define GL_DEBUG_TYPE_OTHER 0x8251
485#endif
486#ifndef GL_DEBUG_TYPE_MARKER
487#define GL_DEBUG_TYPE_MARKER 0x8268
488#endif
489#ifndef GL_DEBUG_TYPE_PUSH_GROUP
490#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269
491#endif
492#ifndef GL_DEBUG_TYPE_POP_GROUP
493#define GL_DEBUG_TYPE_POP_GROUP 0x826A
494#endif
495#ifndef GL_DEBUG_SEVERITY_NOTIFICATION
496#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B
497#endif
498#ifndef GL_MAX_DEBUG_GROUP_STACK_DEPTH
499#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C
500#endif
501#ifndef GL_DEBUG_GROUP_STACK_DEPTH
502#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D
503#endif
504#ifndef GL_BUFFER
505#define GL_BUFFER 0x82E0
506#endif
507#ifndef GL_SHADER
508#define GL_SHADER 0x82E1
509#endif
510#ifndef GL_PROGRAM
511#define GL_PROGRAM 0x82E2
512#endif
513#ifndef GL_QUERY
514#define GL_QUERY 0x82E3
515#endif
516#ifndef GL_PROGRAM_PIPELINE
517#define GL_PROGRAM_PIPELINE 0x82E4
518#endif
519#ifndef GL_SAMPLER
520#define GL_SAMPLER 0x82E6
521#endif
522#ifndef GL_DISPLAY_LIST
523#define GL_DISPLAY_LIST 0x82E7
524#endif
525#ifndef GL_MAX_LABEL_LENGTH
526#define GL_MAX_LABEL_LENGTH 0x82E8
527#endif
528#ifndef GL_MAX_DEBUG_MESSAGE_LENGTH
529#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143
530#endif
531#ifndef GL_MAX_DEBUG_LOGGED_MESSAGES
532#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144
533#endif
534#ifndef GL_DEBUG_LOGGED_MESSAGES
535#define GL_DEBUG_LOGGED_MESSAGES 0x9145
536#endif
537#ifndef GL_DEBUG_SEVERITY_HIGH
538#define GL_DEBUG_SEVERITY_HIGH 0x9146
539#endif
540#ifndef GL_DEBUG_SEVERITY_MEDIUM
541#define GL_DEBUG_SEVERITY_MEDIUM 0x9147
542#endif
543#ifndef GL_DEBUG_SEVERITY_LOW
544#define GL_DEBUG_SEVERITY_LOW 0x9148
545#endif
546#ifndef GL_DEBUG_OUTPUT
547#define GL_DEBUG_OUTPUT 0x92E0
548#endif
549#ifndef GL_CONTEXT_FLAG_DEBUG_BIT
550#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002
551#endif
552#ifndef GL_STACK_OVERFLOW
553#define GL_STACK_OVERFLOW 0x0503
554#endif
555#ifndef GL_STACK_UNDERFLOW
556#define GL_STACK_UNDERFLOW 0x0504
557#endif
558
559typedef void (QOPENGLF_APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const GLvoid *userParam);
560
561#endif /* USE_MANUAL_DEFS */
562
563
564/*!
565 \internal
566*/
567static QOpenGLDebugMessage::Source qt_messageSourceFromGL(GLenum source)
568{
569 switch (source) {
570 case GL_DEBUG_SOURCE_API:
571 return QOpenGLDebugMessage::APISource;
572 case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
573 return QOpenGLDebugMessage::WindowSystemSource;
574 case GL_DEBUG_SOURCE_SHADER_COMPILER:
575 return QOpenGLDebugMessage::ShaderCompilerSource;
576 case GL_DEBUG_SOURCE_THIRD_PARTY:
577 return QOpenGLDebugMessage::ThirdPartySource;
578 case GL_DEBUG_SOURCE_APPLICATION:
579 return QOpenGLDebugMessage::ApplicationSource;
580 case GL_DEBUG_SOURCE_OTHER:
581 return QOpenGLDebugMessage::OtherSource;
582 }
583
584 Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message source from GL");
585 return QOpenGLDebugMessage::OtherSource;
586}
587
588/*!
589 \internal
590*/
591static GLenum qt_messageSourceToGL(QOpenGLDebugMessage::Source source)
592{
593 switch (source) {
594 case QOpenGLDebugMessage::InvalidSource:
595 break;
596 case QOpenGLDebugMessage::APISource:
597 return GL_DEBUG_SOURCE_API;
598 case QOpenGLDebugMessage::WindowSystemSource:
599 return GL_DEBUG_SOURCE_WINDOW_SYSTEM;
600 case QOpenGLDebugMessage::ShaderCompilerSource:
601 return GL_DEBUG_SOURCE_SHADER_COMPILER;
602 case QOpenGLDebugMessage::ThirdPartySource:
603 return GL_DEBUG_SOURCE_THIRD_PARTY;
604 case QOpenGLDebugMessage::ApplicationSource:
605 return GL_DEBUG_SOURCE_APPLICATION;
606 case QOpenGLDebugMessage::OtherSource:
607 return GL_DEBUG_SOURCE_OTHER;
608 case QOpenGLDebugMessage::AnySource:
609 break;
610 }
611
612 Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message source");
613 return GL_DEBUG_SOURCE_OTHER;
614}
615
616/*!
617 \internal
618*/
619static QString qt_messageSourceToString(QOpenGLDebugMessage::Source source)
620{
621 switch (source) {
622 case QOpenGLDebugMessage::InvalidSource:
623 return QStringLiteral("InvalidSource");
624 case QOpenGLDebugMessage::APISource:
625 return QStringLiteral("APISource");
626 case QOpenGLDebugMessage::WindowSystemSource:
627 return QStringLiteral("WindowSystemSource");
628 case QOpenGLDebugMessage::ShaderCompilerSource:
629 return QStringLiteral("ShaderCompilerSource");
630 case QOpenGLDebugMessage::ThirdPartySource:
631 return QStringLiteral("ThirdPartySource");
632 case QOpenGLDebugMessage::ApplicationSource:
633 return QStringLiteral("ApplicationSource");
634 case QOpenGLDebugMessage::OtherSource:
635 return QStringLiteral("OtherSource");
636 case QOpenGLDebugMessage::AnySource:
637 return QStringLiteral("AnySource");
638 }
639
640 Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message source");
641 return QString();
642}
643
644/*!
645 \internal
646*/
647static QOpenGLDebugMessage::Type qt_messageTypeFromGL(GLenum type)
648{
649 switch (type) {
650 case GL_DEBUG_TYPE_ERROR:
651 return QOpenGLDebugMessage::ErrorType;
652 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
653 return QOpenGLDebugMessage::DeprecatedBehaviorType;
654 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
655 return QOpenGLDebugMessage::UndefinedBehaviorType;
656 case GL_DEBUG_TYPE_PORTABILITY:
657 return QOpenGLDebugMessage::PortabilityType;
658 case GL_DEBUG_TYPE_PERFORMANCE:
659 return QOpenGLDebugMessage::PerformanceType;
660 case GL_DEBUG_TYPE_OTHER:
661 return QOpenGLDebugMessage::OtherType;
662 case GL_DEBUG_TYPE_MARKER:
663 return QOpenGLDebugMessage::MarkerType;
664 case GL_DEBUG_TYPE_PUSH_GROUP:
665 return QOpenGLDebugMessage::GroupPushType;
666 case GL_DEBUG_TYPE_POP_GROUP:
667 return QOpenGLDebugMessage::GroupPopType;
668 }
669
670 Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message type from GL");
671 return QOpenGLDebugMessage::OtherType;
672}
673
674/*!
675 \internal
676*/
677static GLenum qt_messageTypeToGL(QOpenGLDebugMessage::Type type)
678{
679 switch (type) {
680 case QOpenGLDebugMessage::InvalidType:
681 break;
682 case QOpenGLDebugMessage::ErrorType:
683 return GL_DEBUG_TYPE_ERROR;
684 case QOpenGLDebugMessage::DeprecatedBehaviorType:
685 return GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR;
686 case QOpenGLDebugMessage::UndefinedBehaviorType:
687 return GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR;
688 case QOpenGLDebugMessage::PortabilityType:
689 return GL_DEBUG_TYPE_PORTABILITY;
690 case QOpenGLDebugMessage::PerformanceType:
691 return GL_DEBUG_TYPE_PERFORMANCE;
692 case QOpenGLDebugMessage::OtherType:
693 return GL_DEBUG_TYPE_OTHER;
694 case QOpenGLDebugMessage::MarkerType:
695 return GL_DEBUG_TYPE_MARKER;
696 case QOpenGLDebugMessage::GroupPushType:
697 return GL_DEBUG_TYPE_PUSH_GROUP;
698 case QOpenGLDebugMessage::GroupPopType:
699 return GL_DEBUG_TYPE_POP_GROUP;
700 case QOpenGLDebugMessage::AnyType:
701 break;
702 }
703
704 Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message type");
705 return GL_DEBUG_TYPE_OTHER;
706}
707
708/*!
709 \internal
710*/
711static QString qt_messageTypeToString(QOpenGLDebugMessage::Type type)
712{
713 switch (type) {
714 case QOpenGLDebugMessage::InvalidType:
715 return QStringLiteral("InvalidType");
716 case QOpenGLDebugMessage::ErrorType:
717 return QStringLiteral("ErrorType");
718 case QOpenGLDebugMessage::DeprecatedBehaviorType:
719 return QStringLiteral("DeprecatedBehaviorType");
720 case QOpenGLDebugMessage::UndefinedBehaviorType:
721 return QStringLiteral("UndefinedBehaviorType");
722 case QOpenGLDebugMessage::PortabilityType:
723 return QStringLiteral("PortabilityType");
724 case QOpenGLDebugMessage::PerformanceType:
725 return QStringLiteral("PerformanceType");
726 case QOpenGLDebugMessage::OtherType:
727 return QStringLiteral("OtherType");
728 case QOpenGLDebugMessage::MarkerType:
729 return QStringLiteral("MarkerType");
730 case QOpenGLDebugMessage::GroupPushType:
731 return QStringLiteral("GroupPushType");
732 case QOpenGLDebugMessage::GroupPopType:
733 return QStringLiteral("GroupPopType");
734 case QOpenGLDebugMessage::AnyType:
735 return QStringLiteral("AnyType");
736 }
737
738 Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message type");
739 return QString();
740}
741
742/*!
743 \internal
744*/
745static QOpenGLDebugMessage::Severity qt_messageSeverityFromGL(GLenum severity)
746{
747 switch (severity) {
748 case GL_DEBUG_SEVERITY_HIGH:
749 return QOpenGLDebugMessage::HighSeverity;
750 case GL_DEBUG_SEVERITY_MEDIUM:
751 return QOpenGLDebugMessage::MediumSeverity;
752 case GL_DEBUG_SEVERITY_LOW:
753 return QOpenGLDebugMessage::LowSeverity;
754 case GL_DEBUG_SEVERITY_NOTIFICATION:
755 return QOpenGLDebugMessage::NotificationSeverity;
756 }
757
758 Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message severity from GL");
759 return QOpenGLDebugMessage::NotificationSeverity;
760}
761
762/*!
763 \internal
764*/
765static GLenum qt_messageSeverityToGL(QOpenGLDebugMessage::Severity severity)
766{
767 switch (severity) {
768 case QOpenGLDebugMessage::InvalidSeverity:
769 break;
770 case QOpenGLDebugMessage::HighSeverity:
771 return GL_DEBUG_SEVERITY_HIGH;
772 case QOpenGLDebugMessage::MediumSeverity:
773 return GL_DEBUG_SEVERITY_MEDIUM;
774 case QOpenGLDebugMessage::LowSeverity:
775 return GL_DEBUG_SEVERITY_LOW;
776 case QOpenGLDebugMessage::NotificationSeverity:
777 return GL_DEBUG_SEVERITY_NOTIFICATION;
778 case QOpenGLDebugMessage::AnySeverity:
779 break;
780 }
781
782 Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message severity");
783 return GL_DEBUG_SEVERITY_NOTIFICATION;
784}
785
786/*!
787 \internal
788*/
789static QString qt_messageSeverityToString(QOpenGLDebugMessage::Severity severity)
790{
791 switch (severity) {
792 case QOpenGLDebugMessage::InvalidSeverity:
793 return QStringLiteral("InvalidSeverity");
794 case QOpenGLDebugMessage::HighSeverity:
795 return QStringLiteral("HighSeverity");
796 case QOpenGLDebugMessage::MediumSeverity:
797 return QStringLiteral("MediumSeverity");
798 case QOpenGLDebugMessage::LowSeverity:
799 return QStringLiteral("LowSeverity");
800 case QOpenGLDebugMessage::NotificationSeverity:
801 return QStringLiteral("NotificationSeverity");
802 case QOpenGLDebugMessage::AnySeverity:
803 return QStringLiteral("AnySeverity");
804 }
805
806 Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message severity");
807 return QString();
808}
809
810class QOpenGLDebugMessagePrivate : public QSharedData
811{
812public:
813 QOpenGLDebugMessagePrivate();
814
815 QString message;
816 GLuint id;
817 QOpenGLDebugMessage::Source source;
818 QOpenGLDebugMessage::Type type;
819 QOpenGLDebugMessage::Severity severity;
820};
821
822/*!
823 \internal
824*/
825QOpenGLDebugMessagePrivate::QOpenGLDebugMessagePrivate()
826 : message(),
827 id(0),
828 source(QOpenGLDebugMessage::InvalidSource),
829 type(QOpenGLDebugMessage::InvalidType),
830 severity(QOpenGLDebugMessage::InvalidSeverity)
831{
832}
833
834
835/*!
836 Constructs a debug message with an empty message string, id set to 0,
837 source set to InvalidSource, type set to InvalidType, and severity set to
838 InvalidSeverity.
839
840 \note This constructor should not be used to create a debug message;
841 instead, use the createApplicationMessage() or the createThirdPartyMessage()
842 static functions.
843
844 \sa createApplicationMessage(), createThirdPartyMessage()
845*/
846QOpenGLDebugMessage::QOpenGLDebugMessage()
847 : d(new QOpenGLDebugMessagePrivate)
848{
849}
850
851/*!
852 Constructs a debug message as a copy of \a debugMessage.
853
854 \sa operator=()
855*/
856QOpenGLDebugMessage::QOpenGLDebugMessage(const QOpenGLDebugMessage &debugMessage)
857 : d(debugMessage.d)
858{
859}
860
861/*!
862 Destroys this debug message.
863*/
864QOpenGLDebugMessage::~QOpenGLDebugMessage()
865{
866}
867
868/*!
869 Assigns the message \a debugMessage to this object, and returns a reference
870 to the copy.
871*/
872QOpenGLDebugMessage &QOpenGLDebugMessage::operator=(const QOpenGLDebugMessage &debugMessage)
873{
874 d = debugMessage.d;
875 return *this;
876}
877
878/*!
879 \fn QOpenGLDebugMessage &QOpenGLDebugMessage::operator=(QOpenGLDebugMessage &&debugMessage)
880
881 Move-assigns \a debugMessage to this object.
882*/
883
884/*!
885 \fn void QOpenGLDebugMessage::swap(QOpenGLDebugMessage &debugMessage)
886
887 Swaps the message \a debugMessage with this message. This operation is very
888 fast and never fails.
889*/
890
891/*!
892 Returns the source of the debug message.
893*/
894QOpenGLDebugMessage::Source QOpenGLDebugMessage::source() const
895{
896 return d->source;
897}
898
899/*!
900 Returns the type of the debug message.
901*/
902QOpenGLDebugMessage::Type QOpenGLDebugMessage::type() const
903{
904 return d->type;
905}
906
907/*!
908 Returns the severity of the debug message.
909*/
910QOpenGLDebugMessage::Severity QOpenGLDebugMessage::severity() const
911{
912 return d->severity;
913}
914
915/*!
916 Returns the id of the debug message. Ids are generally vendor-specific.
917*/
918GLuint QOpenGLDebugMessage::id() const
919{
920 return d->id;
921}
922
923/*!
924 Returns the textual message contained by this debug message.
925*/
926QString QOpenGLDebugMessage::message() const
927{
928 return d->message;
929}
930
931/*!
932 Constructs and returns a debug message with \a text as its text, \a id
933 as id, \a severity as severity, and \a type as type. The message source
934 will be set to ApplicationSource.
935
936 \sa QOpenGLDebugLogger::logMessage(), createThirdPartyMessage()
937*/
938QOpenGLDebugMessage QOpenGLDebugMessage::createApplicationMessage(const QString &text,
939 GLuint id,
940 QOpenGLDebugMessage::Severity severity,
941 QOpenGLDebugMessage::Type type)
942{
943 QOpenGLDebugMessage m;
944 m.d->message = text;
945 m.d->id = id;
946 m.d->severity = severity;
947 m.d->type = type;
948 m.d->source = ApplicationSource;
949 return m;
950}
951
952/*!
953 Constructs and returns a debug message with \a text as its text, \a id
954 as id, \a severity as severity, and \a type as type. The message source
955 will be set to ThirdPartySource.
956
957 \sa QOpenGLDebugLogger::logMessage(), createApplicationMessage()
958*/
959QOpenGLDebugMessage QOpenGLDebugMessage::createThirdPartyMessage(const QString &text,
960 GLuint id,
961 QOpenGLDebugMessage::Severity severity,
962 QOpenGLDebugMessage::Type type)
963{
964 QOpenGLDebugMessage m;
965 m.d->message = text;
966 m.d->id = id;
967 m.d->severity = severity;
968 m.d->type = type;
969 m.d->source = ThirdPartySource;
970 return m;
971}
972
973/*!
974 Returns \c true if this debug message is equal to \a debugMessage, or false
975 otherwise. Two debugging messages are equal if they have the same textual
976 message, the same id, the same source, the same type and the same severity.
977
978 \sa operator!=()
979*/
980bool QOpenGLDebugMessage::operator==(const QOpenGLDebugMessage &debugMessage) const
981{
982 return (d == debugMessage.d)
983 || (d->id == debugMessage.d->id
984 && d->source == debugMessage.d->source
985 && d->type == debugMessage.d->type
986 && d->severity == debugMessage.d->severity
987 && d->message == debugMessage.d->message);
988}
989
990/*!
991 \fn bool QOpenGLDebugMessage::operator!=(const QOpenGLDebugMessage &debugMessage) const
992
993 Returns \c true if this message is different from \a debugMessage, or false
994 otherwise.
995
996 \sa operator==()
997*/
998
999#ifndef QT_NO_DEBUG_STREAM
1000/*!
1001 \relates QOpenGLDebugMessage
1002
1003 Writes the source \a source into the debug object \a debug for debugging
1004 purposes.
1005*/
1006QDebug operator<<(QDebug debug, QOpenGLDebugMessage::Source source)
1007{
1008 QDebugStateSaver saver(debug);
1009 debug.nospace() << "QOpenGLDebugMessage::Source("
1010 << qt_messageSourceToString(source)
1011 << ')';
1012 return debug;
1013}
1014
1015/*!
1016 \relates QOpenGLDebugMessage
1017
1018 Writes the type \a type into the debug object \a debug for debugging
1019 purposes.
1020*/
1021QDebug operator<<(QDebug debug, QOpenGLDebugMessage::Type type)
1022{
1023 QDebugStateSaver saver(debug);
1024 debug.nospace() << "QOpenGLDebugMessage::Type("
1025 << qt_messageTypeToString(type)
1026 << ')';
1027 return debug;
1028}
1029
1030/*!
1031 \relates QOpenGLDebugMessage
1032
1033 Writes the severity \a severity into the debug object \a debug for debugging
1034 purposes.
1035*/
1036QDebug operator<<(QDebug debug, QOpenGLDebugMessage::Severity severity)
1037{
1038 QDebugStateSaver saver(debug);
1039 debug.nospace() << "QOpenGLDebugMessage::Severity("
1040 << qt_messageSeverityToString(severity)
1041 << ')';
1042 return debug;
1043}
1044
1045/*!
1046 \relates QOpenGLDebugMessage
1047
1048 Writes the message \a message into the debug object \a debug for debugging
1049 purposes.
1050*/
1051QDebug operator<<(QDebug debug, const QOpenGLDebugMessage &message)
1052{
1053 QDebugStateSaver saver(debug);
1054 debug.nospace() << "QOpenGLDebugMessage("
1055 << qt_messageSourceToString(message.source()) << ", "
1056 << message.id() << ", "
1057 << message.message() << ", "
1058 << qt_messageSeverityToString(message.severity()) << ", "
1059 << qt_messageTypeToString(message.type()) << ')';
1060 return debug;
1061
1062}
1063#endif // QT_NO_DEBUG_STREAM
1064
1065typedef void (QOPENGLF_APIENTRYP qt_glDebugMessageControl_t)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);
1066typedef void (QOPENGLF_APIENTRYP qt_glDebugMessageInsert_t)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf);
1067typedef void (QOPENGLF_APIENTRYP qt_glDebugMessageCallback_t)(GLDEBUGPROC callback, const void *userParam);
1068typedef GLuint (QOPENGLF_APIENTRYP qt_glGetDebugMessageLog_t)(GLuint count, GLsizei bufsize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog);
1069typedef void (QOPENGLF_APIENTRYP qt_glPushDebugGroup_t)(GLenum source, GLuint id, GLsizei length, const GLchar *message);
1070typedef void (QOPENGLF_APIENTRYP qt_glPopDebugGroup_t)();
1071typedef void (QOPENGLF_APIENTRYP qt_glGetPointerv_t)(GLenum pname, GLvoid **params);
1072
1073class QOpenGLDebugLoggerPrivate : public QObjectPrivate
1074{
1075 Q_DECLARE_PUBLIC(QOpenGLDebugLogger)
1076public:
1077 QOpenGLDebugLoggerPrivate();
1078
1079 void handleMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *rawMessage);
1080 void controlDebugMessages(QOpenGLDebugMessage::Sources sources,
1081 QOpenGLDebugMessage::Types types,
1082 QOpenGLDebugMessage::Severities severities,
1083 const QVector<GLuint> &ids,
1084 const QByteArray &callerName,
1085 bool enable);
1086 void _q_contextAboutToBeDestroyed();
1087
1088 qt_glDebugMessageControl_t glDebugMessageControl;
1089 qt_glDebugMessageInsert_t glDebugMessageInsert;
1090 qt_glDebugMessageCallback_t glDebugMessageCallback;
1091 qt_glGetDebugMessageLog_t glGetDebugMessageLog;
1092 qt_glPushDebugGroup_t glPushDebugGroup;
1093 qt_glPopDebugGroup_t glPopDebugGroup;
1094 qt_glGetPointerv_t glGetPointerv;
1095
1096 GLDEBUGPROC oldDebugCallbackFunction;
1097 void *oldDebugCallbackParameter;
1098 QOpenGLContext *context;
1099 GLint maxMessageLength;
1100 QOpenGLDebugLogger::LoggingMode loggingMode;
1101 bool initialized : 1;
1102 bool isLogging : 1;
1103 bool debugWasEnabled : 1;
1104 bool syncDebugWasEnabled : 1;
1105};
1106
1107/*!
1108 \internal
1109*/
1110QOpenGLDebugLoggerPrivate::QOpenGLDebugLoggerPrivate()
1111 : glDebugMessageControl(0),
1112 glDebugMessageInsert(0),
1113 glDebugMessageCallback(0),
1114 glGetDebugMessageLog(0),
1115 glPushDebugGroup(0),
1116 glPopDebugGroup(0),
1117 oldDebugCallbackFunction(0),
1118 context(0),
1119 maxMessageLength(0),
1120 loggingMode(QOpenGLDebugLogger::AsynchronousLogging),
1121 initialized(false),
1122 isLogging(false),
1123 debugWasEnabled(false),
1124 syncDebugWasEnabled(false)
1125{
1126}
1127
1128/*!
1129 \internal
1130*/
1131void QOpenGLDebugLoggerPrivate::handleMessage(GLenum source,
1132 GLenum type,
1133 GLuint id,
1134 GLenum severity,
1135 GLsizei length,
1136 const GLchar *rawMessage)
1137{
1138 if (oldDebugCallbackFunction)
1139 oldDebugCallbackFunction(source, type, id, severity, length, rawMessage, oldDebugCallbackParameter);
1140
1141 QOpenGLDebugMessage message;
1142
1143 QOpenGLDebugMessagePrivate *messagePrivate = message.d.data();
1144 messagePrivate->source = qt_messageSourceFromGL(source);
1145 messagePrivate->type = qt_messageTypeFromGL(type);
1146 messagePrivate->id = id;
1147 messagePrivate->severity = qt_messageSeverityFromGL(severity);
1148 // not passing the length to fromUtf8, as some bugged OpenGL drivers
1149 // do not handle the length correctly. Just rely on the message to be NUL terminated.
1150 messagePrivate->message = QString::fromUtf8(rawMessage);
1151
1152 Q_Q(QOpenGLDebugLogger);
1153 emit q->messageLogged(message);
1154}
1155
1156/*!
1157 \internal
1158*/
1159void QOpenGLDebugLoggerPrivate::controlDebugMessages(QOpenGLDebugMessage::Sources sources,
1160 QOpenGLDebugMessage::Types types,
1161 QOpenGLDebugMessage::Severities severities,
1162 const QVector<GLuint> &ids,
1163 const QByteArray &callerName,
1164 bool enable)
1165{
1166 if (!initialized) {
1167 qWarning("QOpenGLDebugLogger::%s(): object must be initialized before enabling/disabling messages", callerName.constData());
1168 return;
1169 }
1170 if (sources == QOpenGLDebugMessage::InvalidSource) {
1171 qWarning("QOpenGLDebugLogger::%s(): invalid source specified", callerName.constData());
1172 return;
1173 }
1174 if (types == QOpenGLDebugMessage::InvalidType) {
1175 qWarning("QOpenGLDebugLogger::%s(): invalid type specified", callerName.constData());
1176 return;
1177 }
1178 if (severities == QOpenGLDebugMessage::InvalidSeverity) {
1179 qWarning("QOpenGLDebugLogger::%s(): invalid severity specified", callerName.constData());
1180 return;
1181 }
1182
1183 QVarLengthArray<GLenum, 8> glSources;
1184 QVarLengthArray<GLenum, 8> glTypes;
1185 QVarLengthArray<GLenum, 8> glSeverities;
1186
1187 if (ids.count() > 0) {
1188 Q_ASSERT(severities == QOpenGLDebugMessage::AnySeverity);
1189
1190 // The GL_KHR_debug extension says:
1191 //
1192 // - If <count> is greater than zero, then <ids> is an array of <count>
1193 // message IDs for the specified combination of <source> and <type>. In
1194 // this case, if <source> or <type> is DONT_CARE, or <severity> is not
1195 // DONT_CARE, the error INVALID_OPERATION is generated. If <count> is
1196 // zero, the value if <ids> is ignored.
1197 //
1198 // This means we can't convert AnySource or AnyType into DONT_CARE, but we have to roll
1199 // them into individual sources/types.
1200
1201 if (sources == QOpenGLDebugMessage::AnySource) {
1202 sources = QOpenGLDebugMessage::InvalidSource;
1203 for (uint i = 1; i <= QOpenGLDebugMessage::LastSource; i = i << 1)
1204 sources |= QOpenGLDebugMessage::Source(i);
1205 }
1206
1207 if (types == QOpenGLDebugMessage::AnyType) {
1208 types = QOpenGLDebugMessage::InvalidType;
1209 for (uint i = 1; i <= QOpenGLDebugMessage::LastType; i = i << 1)
1210 types |= QOpenGLDebugMessage::Type(i);
1211 }
1212 }
1213
1214#define CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(type, source, target) \
1215 if (source == QOpenGLDebugMessage::Any ## type) { \
1216 target << GL_DONT_CARE; \
1217 } else { \
1218 for (uint i = 1; i <= QOpenGLDebugMessage::Last ## type; i = i << 1) \
1219 if (source.testFlag(QOpenGLDebugMessage:: type (i))) \
1220 target << qt_message ## type ## ToGL (QOpenGLDebugMessage:: type (i)); \
1221 }
1222
1223 CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(Source, sources, glSources)
1224 CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(Type, types, glTypes)
1225 CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(Severity, severities, glSeverities)
1226#undef CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS
1227
1228 const GLsizei idCount = ids.count();
1229 // The GL_KHR_debug extension says that if idCount is 0, idPtr must be ignored.
1230 // Unfortunately, some bugged drivers do NOT ignore it, so pass NULL in case.
1231 const GLuint * const idPtr = idCount ? ids.constData() : 0;
1232
1233 for (GLenum source : glSources)
1234 for (GLenum type : glTypes)
1235 for (GLenum severity : glSeverities)
1236 glDebugMessageControl(source, type, severity, idCount, idPtr, GLboolean(enable));
1237}
1238
1239/*!
1240 \internal
1241*/
1242void QOpenGLDebugLoggerPrivate::_q_contextAboutToBeDestroyed()
1243{
1244 Q_ASSERT(context);
1245
1246 // Re-make our context current somehow, otherwise stopLogging will fail.
1247
1248 // Save the current context and its surface in case we need to set them back
1249 QOpenGLContext *currentContext = QOpenGLContext::currentContext();
1250 QSurface *currentSurface = 0;
1251
1252 QScopedPointer<QOffscreenSurface> offscreenSurface;
1253
1254 if (context != currentContext) {
1255 // Make our old context current on a temporary surface
1256 if (currentContext)
1257 currentSurface = currentContext->surface();
1258
1259 offscreenSurface.reset(new QOffscreenSurface);
1260 offscreenSurface->setFormat(context->format());
1261 offscreenSurface->create();
1262 if (!context->makeCurrent(offscreenSurface.data()))
1263 qWarning("QOpenGLDebugLoggerPrivate::_q_contextAboutToBeDestroyed(): could not make the owning GL context current for cleanup");
1264 }
1265
1266 Q_Q(QOpenGLDebugLogger);
1267 q->stopLogging();
1268
1269 if (offscreenSurface) {
1270 // We did change the current context: set it back
1271 if (currentContext)
1272 currentContext->makeCurrent(currentSurface);
1273 else
1274 context->doneCurrent();
1275 }
1276
1277 QObject::disconnect(context, SIGNAL(aboutToBeDestroyed()), q, SLOT(_q_contextAboutToBeDestroyed()));
1278 context = 0;
1279 initialized = false;
1280}
1281
1282extern "C" {
1283static void QOPENGLF_APIENTRY qt_opengl_debug_callback(GLenum source,
1284 GLenum type,
1285 GLuint id,
1286 GLenum severity,
1287 GLsizei length,
1288 const GLchar *rawMessage,
1289 const GLvoid *userParam)
1290{
1291 QOpenGLDebugLoggerPrivate *loggerPrivate = static_cast<QOpenGLDebugLoggerPrivate *>(const_cast<GLvoid *>(userParam));
1292 loggerPrivate->handleMessage(source, type, id, severity, length, rawMessage);
1293}
1294}
1295
1296/*!
1297 Constructs a new logger object with the given \a parent.
1298
1299 \note The object must be initialized before logging can happen.
1300
1301 \sa initialize()
1302*/
1303QOpenGLDebugLogger::QOpenGLDebugLogger(QObject *parent)
1304 : QObject(*new QOpenGLDebugLoggerPrivate, parent)
1305{
1306 // QOpenGLDebugMessage is going to be mostly used as an argument
1307 // of a cross thread connection, therefore let's ease the life for the users
1308 // and register the type for them.
1309 qRegisterMetaType<QOpenGLDebugMessage>();
1310}
1311
1312/*!
1313 Destroys the logger object.
1314*/
1315QOpenGLDebugLogger::~QOpenGLDebugLogger()
1316{
1317 stopLogging();
1318}
1319
1320/*!
1321 Initializes the object in the current OpenGL context. The context must
1322 support the \c{GL_KHR_debug} extension for the initialization to succeed.
1323 The object must be initialized before any logging can happen.
1324
1325 It is safe to call this function multiple times from the same context.
1326
1327 This function can also be used to change the context of a previously
1328 initialized object; note that in this case the object must not be logging
1329 when you call this function.
1330
1331 Returns \c true if the logger is successfully initialized; false otherwise.
1332
1333 \sa QOpenGLContext
1334*/
1335bool QOpenGLDebugLogger::initialize()
1336{
1337 QOpenGLContext *context = QOpenGLContext::currentContext();
1338 if (!context) {
1339 qWarning("QOpenGLDebugLogger::initialize(): no current OpenGL context found.");
1340 return false;
1341 }
1342
1343 Q_D(QOpenGLDebugLogger);
1344 if (d->context == context) {
1345 // context is non-NULL, d->context is non NULL only on successful initialization.
1346 Q_ASSERT(d->initialized);
1347 return true;
1348 }
1349
1350 if (d->isLogging) {
1351 qWarning("QOpenGLDebugLogger::initialize(): cannot initialize the object while logging. Please stop the logging first.");
1352 return false;
1353 }
1354
1355 if (d->context)
1356 disconnect(d->context, SIGNAL(aboutToBeDestroyed()), this, SLOT(_q_contextAboutToBeDestroyed()));
1357
1358 d->initialized = false;
1359 d->context = 0;
1360
1361 if (!context->hasExtension(QByteArrayLiteral("GL_KHR_debug")))
1362 return false;
1363
1364 d->context = context;
1365 connect(d->context, SIGNAL(aboutToBeDestroyed()), this, SLOT(_q_contextAboutToBeDestroyed()));
1366
1367#define GET_DEBUG_PROC_ADDRESS(procName) \
1368 d->procName = reinterpret_cast< qt_ ## procName ## _t >( \
1369 d->context->getProcAddress(d->context->isOpenGLES() ? (#procName "KHR") : (#procName)) \
1370 );
1371
1372 GET_DEBUG_PROC_ADDRESS(glDebugMessageControl);
1373 GET_DEBUG_PROC_ADDRESS(glDebugMessageInsert);
1374 GET_DEBUG_PROC_ADDRESS(glDebugMessageCallback);
1375 GET_DEBUG_PROC_ADDRESS(glGetDebugMessageLog);
1376 GET_DEBUG_PROC_ADDRESS(glPushDebugGroup);
1377 GET_DEBUG_PROC_ADDRESS(glPopDebugGroup);
1378 GET_DEBUG_PROC_ADDRESS(glGetPointerv)
1379
1380#undef GET_DEBUG_PROC_ADDRESS
1381
1382 QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_MAX_DEBUG_MESSAGE_LENGTH, &d->maxMessageLength);
1383
1384#ifndef QT_NO_DEBUG
1385 if (!d->context->format().testOption(QSurfaceFormat::DebugContext)) {
1386 qWarning("QOpenGLDebugLogger::initialize(): the current context is not a debug context:\n"
1387 " this means that the GL may not generate any debug output at all.\n"
1388 " To avoid this warning, try creating the context with the\n"
1389 " QSurfaceFormat::DebugContext surface format option.");
1390 }
1391#endif // QT_NO_DEBUG
1392
1393 d->initialized = true;
1394 return true;
1395}
1396
1397/*!
1398 Returns \c true if this object is currently logging, false otherwise.
1399
1400 \sa startLogging()
1401*/
1402bool QOpenGLDebugLogger::isLogging() const
1403{
1404 Q_D(const QOpenGLDebugLogger);
1405 return d->isLogging;
1406}
1407
1408/*!
1409 Starts logging messages coming from the OpenGL server. When a new message
1410 is received, the signal messageLogged() is emitted, carrying the logged
1411 message as argument.
1412
1413 \a loggingMode specifies whether the logging must be asynchronous (the default)
1414 or synchronous.
1415
1416 QOpenGLDebugLogger will record the values of \c{GL_DEBUG_OUTPUT} and
1417 \c{GL_DEBUG_OUTPUT_SYNCHRONOUS} when logging is started, and set them back
1418 when logging is stopped. Moreover, any user-defined OpenGL debug callback
1419 installed when this function is invoked will be restored when logging is
1420 stopped; QOpenGLDebugLogger will ensure that the pre-existing callback will
1421 still be invoked when logging.
1422
1423 \note It's not possible to change the logging mode without stopping and
1424 starting logging again. This might change in a future version of Qt.
1425
1426 \note The object must be initialized before logging can happen.
1427
1428 \sa stopLogging(), initialize()
1429*/
1430void QOpenGLDebugLogger::startLogging(QOpenGLDebugLogger::LoggingMode loggingMode)
1431{
1432 Q_D(QOpenGLDebugLogger);
1433 if (!d->initialized) {
1434 qWarning("QOpenGLDebugLogger::startLogging(): object must be initialized before logging can start");
1435 return;
1436 }
1437 if (d->isLogging) {
1438 qWarning("QOpenGLDebugLogger::startLogging(): this object is already logging");
1439 return;
1440 }
1441
1442 d->isLogging = true;
1443 d->loggingMode = loggingMode;
1444
1445 d->glGetPointerv(GL_DEBUG_CALLBACK_FUNCTION, reinterpret_cast<void **>(&d->oldDebugCallbackFunction));
1446 d->glGetPointerv(GL_DEBUG_CALLBACK_USER_PARAM, &d->oldDebugCallbackParameter);
1447
1448 d->glDebugMessageCallback(&qt_opengl_debug_callback, d);
1449
1450 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
1451 d->debugWasEnabled = funcs->glIsEnabled(GL_DEBUG_OUTPUT);
1452 d->syncDebugWasEnabled = funcs->glIsEnabled(GL_DEBUG_OUTPUT_SYNCHRONOUS);
1453
1454 if (d->loggingMode == SynchronousLogging)
1455 funcs->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
1456 else
1457 funcs->glDisable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
1458
1459 funcs->glEnable(GL_DEBUG_OUTPUT);
1460}
1461
1462/*!
1463 Returns the logging mode of the object.
1464
1465 \sa startLogging()
1466*/
1467QOpenGLDebugLogger::LoggingMode QOpenGLDebugLogger::loggingMode() const
1468{
1469 Q_D(const QOpenGLDebugLogger);
1470 return d->loggingMode;
1471}
1472
1473/*!
1474 Stops logging messages from the OpenGL server.
1475
1476 \sa startLogging()
1477*/
1478void QOpenGLDebugLogger::stopLogging()
1479{
1480 Q_D(QOpenGLDebugLogger);
1481 if (!d->isLogging)
1482 return;
1483
1484 QOpenGLContext *currentContext = QOpenGLContext::currentContext();
1485 if (!currentContext || currentContext != d->context) {
1486 qWarning("QOpenGLDebugLogger::stopLogging(): attempting to stop logging with the wrong OpenGL context current");
1487 return;
1488 }
1489
1490 d->isLogging = false;
1491
1492 d->glDebugMessageCallback(d->oldDebugCallbackFunction, d->oldDebugCallbackParameter);
1493
1494 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
1495 if (!d->debugWasEnabled)
1496 funcs->glDisable(GL_DEBUG_OUTPUT);
1497
1498 if (d->syncDebugWasEnabled)
1499 funcs->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
1500 else
1501 funcs->glDisable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
1502}
1503
1504/*!
1505 Inserts the message \a debugMessage into the OpenGL debug log. This provides
1506 a way for applications or libraries to insert custom messages that can
1507 ease the debugging of OpenGL applications.
1508
1509 \note \a debugMessage must have QOpenGLDebugMessage::ApplicationSource or
1510 QOpenGLDebugMessage::ThirdPartySource as its source, and a valid
1511 type and severity, otherwise it will not be inserted into the log.
1512
1513 \note The object must be initialized before logging can happen.
1514
1515 \sa initialize()
1516*/
1517void QOpenGLDebugLogger::logMessage(const QOpenGLDebugMessage &debugMessage)
1518{
1519 Q_D(QOpenGLDebugLogger);
1520 if (!d->initialized) {
1521 qWarning("QOpenGLDebugLogger::logMessage(): object must be initialized before logging messages");
1522 return;
1523 }
1524 if (debugMessage.source() != QOpenGLDebugMessage::ApplicationSource
1525 && debugMessage.source() != QOpenGLDebugMessage::ThirdPartySource) {
1526 qWarning("QOpenGLDebugLogger::logMessage(): using a message source different from ApplicationSource\n"
1527 " or ThirdPartySource is not supported by GL_KHR_debug. The message will not be logged.");
1528 return;
1529 }
1530 if (debugMessage.type() == QOpenGLDebugMessage::InvalidType
1531 || debugMessage.type() == QOpenGLDebugMessage::AnyType
1532 || debugMessage.severity() == QOpenGLDebugMessage::InvalidSeverity
1533 || debugMessage.severity() == QOpenGLDebugMessage::AnySeverity) {
1534 qWarning("QOpenGLDebugLogger::logMessage(): the message has a non-valid type and/or severity. The message will not be logged.");
1535 return;
1536 }
1537
1538 const GLenum source = qt_messageSourceToGL(debugMessage.source());
1539 const GLenum type = qt_messageTypeToGL(debugMessage.type());
1540 const GLenum severity = qt_messageSeverityToGL(debugMessage.severity());
1541 QByteArray rawMessage = debugMessage.message().toUtf8();
1542 rawMessage.append('\0');
1543
1544 if (rawMessage.length() > d->maxMessageLength) {
1545 qWarning("QOpenGLDebugLogger::logMessage(): message too long, truncating it\n"
1546 " (%d bytes long, but the GL accepts up to %d bytes)", rawMessage.length(), d->maxMessageLength);
1547 rawMessage.resize(d->maxMessageLength - 1);
1548 rawMessage.append('\0');
1549 }
1550
1551 // Don't pass rawMessage.length(), as unfortunately bugged
1552 // OpenGL drivers will eat the trailing NUL in the message. Just rely
1553 // on the message being NUL terminated.
1554 d->glDebugMessageInsert(source,
1555 type,
1556 debugMessage.id(),
1557 severity,
1558 -1,
1559 rawMessage.constData());
1560}
1561
1562/*!
1563 Pushes a debug group with name \a name, id \a id, and source \a source onto
1564 the debug groups stack. If the group is successfully pushed, OpenGL will
1565 automatically log a message with message \a name, id \a id, source \a
1566 source, type QOpenGLDebugMessage::GroupPushType and severity
1567 QOpenGLDebugMessage::NotificationSeverity.
1568
1569 The newly pushed group will inherit the same filtering settings of the
1570 group that was on the top of the stack; that is, the filtering will not be
1571 changed by pushing a new group.
1572
1573 \note The \a source must either be QOpenGLDebugMessage::ApplicationSource or
1574 QOpenGLDebugMessage::ThirdPartySource, otherwise the group will not be pushed.
1575
1576 \note The object must be initialized before managing debug groups.
1577
1578 \sa popGroup(), enableMessages(), disableMessages()
1579*/
1580void QOpenGLDebugLogger::pushGroup(const QString &name, GLuint id, QOpenGLDebugMessage::Source source)
1581{
1582 Q_D(QOpenGLDebugLogger);
1583 if (!d->initialized) {
1584 qWarning("QOpenGLDebugLogger::pushGroup(): object must be initialized before pushing a debug group");
1585 return;
1586 }
1587 if (source != QOpenGLDebugMessage::ApplicationSource
1588 && source != QOpenGLDebugMessage::ThirdPartySource) {
1589 qWarning("QOpenGLDebugLogger::pushGroup(): using a source different from ApplicationSource\n"
1590 " or ThirdPartySource is not supported by GL_KHR_debug. The group will not be pushed.");
1591 return;
1592 }
1593
1594 QByteArray rawName = name.toUtf8();
1595 rawName.append('\0');
1596 if (rawName.length() > d->maxMessageLength) {
1597 qWarning("QOpenGLDebugLogger::pushGroup(): group name too long, truncating it\n"
1598 " (%d bytes long, but the GL accepts up to %d bytes)", rawName.length(), d->maxMessageLength);
1599 rawName.resize(d->maxMessageLength - 1);
1600 rawName.append('\0');
1601 }
1602
1603 // Don't pass rawMessage.length(), as unfortunately bugged
1604 // OpenGL drivers will eat the trailing NUL in the name. Just rely
1605 // on the name being NUL terminated.
1606 d->glPushDebugGroup(qt_messageSourceToGL(source), id, -1, rawName.constData());
1607}
1608
1609/*!
1610 Pops the topmost debug group from the debug groups stack. If the group is
1611 successfully popped, OpenGL will automatically log a message with message,
1612 id and source matching those of the popped group, type
1613 QOpenGLDebugMessage::GroupPopType and severity
1614 QOpenGLDebugMessage::NotificationSeverity.
1615
1616 Popping a debug group will restore the message filtering settings of the
1617 group that becomes the top of the debug groups stack.
1618
1619 \note The object must be initialized before managing debug groups.
1620
1621 \sa pushGroup()
1622*/
1623void QOpenGLDebugLogger::popGroup()
1624{
1625 Q_D(QOpenGLDebugLogger);
1626 if (!d->initialized) {
1627 qWarning("QOpenGLDebugLogger::pushGroup(): object must be initialized before popping a debug group");
1628 return;
1629 }
1630
1631 d->glPopDebugGroup();
1632}
1633
1634/*!
1635 Enables the logging of messages from the given \a sources, of the given \a
1636 types and with the given \a severities and any message id.
1637
1638 The logging will be enabled in the current control group.
1639
1640 \sa disableMessages(), pushGroup(), popGroup()
1641*/
1642void QOpenGLDebugLogger::enableMessages(QOpenGLDebugMessage::Sources sources,
1643 QOpenGLDebugMessage::Types types,
1644 QOpenGLDebugMessage::Severities severities)
1645{
1646 Q_D(QOpenGLDebugLogger);
1647 d->controlDebugMessages(sources,
1648 types,
1649 severities,
1650 QVector<GLuint>(),
1651 QByteArrayLiteral("enableMessages"),
1652 true);
1653}
1654
1655/*!
1656 Enables the logging of messages with the given \a ids, from the given \a
1657 sources and of the given \a types and any severity.
1658
1659 The logging will be enabled in the current control group.
1660
1661 \sa disableMessages(), pushGroup(), popGroup()
1662*/
1663void QOpenGLDebugLogger::enableMessages(const QVector<GLuint> &ids,
1664 QOpenGLDebugMessage::Sources sources,
1665 QOpenGLDebugMessage::Types types)
1666{
1667 Q_D(QOpenGLDebugLogger);
1668 d->controlDebugMessages(sources,
1669 types,
1670 QOpenGLDebugMessage::AnySeverity,
1671 ids,
1672 QByteArrayLiteral("enableMessages"),
1673 true);
1674}
1675
1676/*!
1677 Disables the logging of messages with the given \a sources, of the given \a
1678 types and with the given \a severities and any message id.
1679
1680 The logging will be disabled in the current control group.
1681
1682 \sa enableMessages(), pushGroup(), popGroup()
1683*/
1684void QOpenGLDebugLogger::disableMessages(QOpenGLDebugMessage::Sources sources,
1685 QOpenGLDebugMessage::Types types,
1686 QOpenGLDebugMessage::Severities severities)
1687{
1688 Q_D(QOpenGLDebugLogger);
1689 d->controlDebugMessages(sources,
1690 types,
1691 severities,
1692 QVector<GLuint>(),
1693 QByteArrayLiteral("disableMessages"),
1694 false);
1695}
1696
1697/*!
1698 Disables the logging of messages with the given \a ids, from the given \a
1699 sources and of the given \a types and any severity.
1700
1701 The logging will be disabled in the current control group.
1702
1703 \sa enableMessages(), pushGroup(), popGroup()
1704*/
1705void QOpenGLDebugLogger::disableMessages(const QVector<GLuint> &ids,
1706 QOpenGLDebugMessage::Sources sources,
1707 QOpenGLDebugMessage::Types types)
1708{
1709 Q_D(QOpenGLDebugLogger);
1710 d->controlDebugMessages(sources,
1711 types,
1712 QOpenGLDebugMessage::AnySeverity,
1713 ids,
1714 QByteArrayLiteral("disableMessages"),
1715 false);
1716}
1717
1718/*!
1719 Reads all the available messages in the OpenGL internal debug log and
1720 returns them. Moreover, this function will clear the internal debug log,
1721 so that subsequent invocations will not return messages that were
1722 already returned.
1723
1724 \sa startLogging()
1725*/
1726QList<QOpenGLDebugMessage> QOpenGLDebugLogger::loggedMessages() const
1727{
1728 Q_D(const QOpenGLDebugLogger);
1729 if (!d->initialized) {
1730 qWarning("QOpenGLDebugLogger::loggedMessages(): object must be initialized before reading logged messages");
1731 return QList<QOpenGLDebugMessage>();
1732 }
1733
1734 static const GLuint maxMessageCount = 128;
1735 GLuint messagesRead;
1736 GLenum messageSources[maxMessageCount];
1737 GLenum messageTypes[maxMessageCount];
1738 GLuint messageIds[maxMessageCount];
1739 GLenum messageSeverities[maxMessageCount];
1740 GLsizei messageLengths[maxMessageCount];
1741
1742 QByteArray messagesBuffer;
1743 messagesBuffer.resize(maxMessageCount * d->maxMessageLength);
1744
1745 QList<QOpenGLDebugMessage> messages;
1746 do {
1747 messagesRead = d->glGetDebugMessageLog(maxMessageCount,
1748 GLsizei(messagesBuffer.size()),
1749 messageSources,
1750 messageTypes,
1751 messageIds,
1752 messageSeverities,
1753 messageLengths,
1754 messagesBuffer.data());
1755
1756 const char *messagesBufferPtr = messagesBuffer.constData();
1757 for (GLuint i = 0; i < messagesRead; ++i) {
1758 QOpenGLDebugMessage message;
1759
1760 QOpenGLDebugMessagePrivate *messagePrivate = message.d.data();
1761 messagePrivate->source = qt_messageSourceFromGL(messageSources[i]);
1762 messagePrivate->type = qt_messageTypeFromGL(messageTypes[i]);
1763 messagePrivate->id = messageIds[i];
1764 messagePrivate->severity = qt_messageSeverityFromGL(messageSeverities[i]);
1765 messagePrivate->message = QString::fromUtf8(messagesBufferPtr, messageLengths[i] - 1);
1766
1767 messagesBufferPtr += messageLengths[i];
1768 messages << message;
1769 }
1770 } while (messagesRead == maxMessageCount);
1771
1772 return messages;
1773}
1774
1775/*!
1776 \fn void QOpenGLDebugLogger::messageLogged(const QOpenGLDebugMessage &debugMessage)
1777
1778 This signal is emitted when a debug message (wrapped by the \a debugMessage
1779 argument) is logged from the OpenGL server.
1780
1781 Depending on the OpenGL implementation, this signal can be emitted
1782 from other threads than the one(s) the receiver(s) lives in, and even
1783 different from the thread the QOpenGLContext in which this object has
1784 been initialized lives in. Moreover, the signal could be emitted from
1785 multiple threads at the same time. This is normally not a problem,
1786 as Qt will utilize a queued connection for cross-thread signal emissions,
1787 but if you force the connection type to Direct then you must be aware of
1788 the potential races in the slots connected to this signal.
1789
1790 If logging have been started in SynchronousLogging mode, OpenGL guarantees
1791 that this signal will be emitted from the same thread the QOpenGLContext
1792 has been bound to, and no concurrent invocations will ever happen.
1793
1794 \note Logging must have been started, or this signal will not be emitted.
1795
1796 \sa startLogging()
1797*/
1798
1799/*!
1800 Returns the maximum supported length, in bytes, for the text of the messages
1801 passed to logMessage(). This is also the maximum length of a debug group
1802 name, as pushing or popping groups will automatically log a message with
1803 the debug group name as the message text.
1804
1805 If a message text is too long, it will be automatically truncated by
1806 QOpenGLDebugLogger.
1807
1808 \note Message texts are encoded in UTF-8 when they get passed to OpenGL, so
1809 their size in bytes does not usually match the amount of UTF-16 code units,
1810 as returned f.i. by QString::length(). (It does if the message contains
1811 7-bit ASCII only data, which is typical for debug messages.)
1812*/
1813qint64 QOpenGLDebugLogger::maximumMessageLength() const
1814{
1815 Q_D(const QOpenGLDebugLogger);
1816 if (!d->initialized) {
1817 qWarning("QOpenGLDebugLogger::maximumMessageLength(): object must be initialized before reading the maximum message length");
1818 return -1;
1819 }
1820 return d->maxMessageLength;
1821}
1822
1823
1824QT_END_NAMESPACE
1825
1826#include "moc_qopengldebug.cpp"
1827